英文:
How to access a tag in xml that ends with specific text using ends-with
问题
我有一个示例的XML文件。
<?xml version="1.0" ?>
<Details date="2022-02-09" ver="1">
<VerNum>/14</VerNum>
<Info>
<model>S22</model>
<branch name="city_1">
<prevstock>10000</prevstock>
<def>1</def>
</branch>
<branch name="city_2">
<presstock>2000</presstock>
<def>2</def>
</branch>
<branch name="city_3">
<futstock>3000</futstock>
<def>0.3</def>
</branch>
</Info>
</Details>
我需要访问库存,标签名称不一定一致,而且我不能依赖节点的位置。我不理解XPath的ends-with
函数的正确用法。
use warnings;
use strict;
use feature 'say';
use Data::Dumper;
use XML::LibXML;
my $file = "ex.xml";
my $parser = XML::LibXML->load_xml(location => $file);
my %branch_stock;
foreach my $sec ($parser->findnodes('/Details/Info')) {
for my $branch ($sec->findnodes('./branch')) {
my $branch_name = $branch->getAttribute('name');
my $stock_value = $branch->findnodes('*[ends-with(name(),"stock")][1]')->[0]->textContent;
$branch_stock{$branch_name} = $stock_value;
}
}
say Dumper \%branch_stock;
这给我一个错误:
error : xmlXPathCompOpEval: function ends-with not found
XPath error : Unregistered function
XPath error : Stack usage errror
error : xmlXPathCompiledEval: 2 objects left on the stack.
请问有谁能帮助我理解问题并解决它呢?非常感谢。
英文:
I have an example xml
<?xml version="1.0" ?>
<Details date="2022-02-09" ver="1">
<VerNum>/14</VerNum>
<Info>
<model>S22</model>
<branch name="city_1">
<prevstock>10000</prevstock>
<def>1</def>
</branch>
<branch name="city_2">
<presstock>2000</presstock>
<def>2</def>
</branch>
<branch name="city_3">
<futstock>3000</futstock>
<def>0.3</def>
</branch>
</Info>
</Details>
I need to access the stock, tag name is not always consistent and I can't depend on position of node too, I do not understand the correct usage of XPath / ends-with function.
use warnings;
use strict;
use feature 'say';
use Data::Dumper;
use XML::LibXML;
my $file = "ex.xml";#// die "Usage: $0 filename\n";
my $parser = XML::LibXML->load_xml(location => $file);
my %branch_stock;
foreach my $sec ($parser->findnodes('/Details/Info')) {
for my $branch ($sec->findnodes('./branch')) {
my $branch_name = $branch->getAttribute('name');
my $stock_value = $branch->findnodes('*[ends-with(name(),"stock")]')->[0]->textContent;
#say "$branch_name --> $stock_value";
$branch_stock{$branch_name} = $stock_value;
}
}
say Dumper \%branch_stock;
This gives me error,
error : xmlXPathCompOpEval: function ends-with not found
XPath error : Unregistered function
XPath error : Stack usage errror
error : xmlXPathCompiledEval: 2 objects left on the stack.
Could anyone please help understand the problem and help overcome please ?
Thanks a lot in advance.
答案1
得分: 4
使用这个XPath1表达式,它模仿了fn:ends-with()
函数:
//branch/node()[substring(local-name(),
string-length(local-name()) - string-length("stock") + 1) = "stock"]
所以:
my $stock_value = $branch
->findnodes('node()[substring(local-name(),
string-length(
local-name()) - string-length("stock") + 1) = "stock"]'
)->[0]->textContent;
来自wikipedia(不幸的是...):
有几个XPath的版本在使用中。XPath 1.0于1999年发布,XPath 2.0于2007年发布(2010年出版了第二版),XPath 3.0于2014年发布,XPath 3.1于2017年发布。然而,XPath 1.0仍然是最广泛使用的版本。
相关链接: https://stackoverflow.com/a/40935676/465183
英文:
Use this XPath1
expression, that mimic the fn:ends-with()
¹ function:
//branch/node()[substring(local-name(),
string-length(local-name()) - string-length("stock") + 1) = "stock"]
So:
my $stock_value = $branch
->findnodes('node()[substring(local-name(),
string-length(
local-name()) - string-length("stock") + 1) = "stock"]'
)->[0]->textContent;
From wikipedia (sadly...)
> There are several versions of XPath in use. XPath 1.0 was published in 1999, XPath 2.0 in 2007 (with a second edition in 2010), XPath 3.0 in 2014, and XPath 3.1 in 2017. However, XPath 1.0 is still the version that is most widely available.
答案2
得分: 2
以下是翻译好的内容:
"That nice ends-with()
, along with many other features, is in XPath2 (and later). In XML::LibXML we are limited to XPath 1.0, as the underlying libxml2
is."
"ends-with(),以及其他许多功能,都在XPath2(和[更高版本](https://www.w3.org/TR/xpath-3/))中。在XML::LibXML中,我们受限于XPath 1.0,因为底层的
libxml2`也是如此。"
"One workable function for querying by partial text is contains
"
"用于通过部分文本查询的一个可行函数是contains
"
这里是一些Perl代码,用于在XML文档中查询数据,并输出结果。此代码使用XML::LibXML模块来解析XML文件并执行XPath查询。在XPath查询中,contains
函数用于查找包含特定文本的元素。然后,代码遍历结果并输出相应的信息。
如果你有更多问题或需要进一步的翻译,请告诉我。
英文:
That nice ends-with()
, along with many other features, is in XPath2 (and later). In XML::LibXML we are limited to XPath 1.0, as the underlying libxml2
is.
One workable function for querying by partial text is contains
use warnings;
use strict;
use feature 'say';
use Data::Dumper;
use XML::LibXML;
my $file = shift // die "Usage: $0 file\n";
my $parser = XML::LibXML->load_xml(location => $file);
my %branch_stock;
for my $sec ($parser->findnodes('/Details/Info')) {
for my $branch ($sec->findnodes('./branch')) {
my $branch_name = $branch->getAttribute('name');
for my $stock ($branch->findnodes('*[contains(name(),"stock")]')) {
say "$branch_name --> $stock";
$branch_stock{$branch_name} = $stock->textContent;
# No "ends-with" in XPath1, what we have here
# $branch->findnodes('*[ends-with(name(),"stock")]')
}
}
}
print Dumper \%branch_stock;
This prints
<!-- language: lang-none -->
city_1 --> <prevstock>10000</prevstock>
city_2 --> <presstock>2000</presstock>
city_3 --> <futstock>3000</futstock>
$VAR1 = {
'city_3' => '3000',
'city_1' => '10000',
'city_2' => '2000'
};
A word on sources and documentation, which I find not so easy for XPath.
There is an overview in perl-libxml-by-example. While XPath 1.0 lacks powerful features of later versions, it does have a scoop of functions. One can also create custom functions using Perl API for that. The library, XML::LibXML
, uses XML::LibXML::XPathContext.
If there is indeed one stock
under each branch
, clearly expected here, we don't need a loop but can pick the "first" (only) element
for my $sec ($parser->findnodes('/Details/Info')) {
for my $branch ($sec->findnodes('./branch')) {
my $branch_name = $branch->getAttribute('name');
my $stock = $branch->findnodes('*[contains(name(),"stock")]')->[0];
say "$branch_name --> $stock";
$branch_stock{$branch_name} = $stock->textContent;
}
}
And there is a shortcut for that, findvalue
for my $sec ($parser->findnodes('/Details/Info')) {
for my $branch ($sec->findnodes('./branch')) {
my $branch_name = $branch->getAttribute('name');
my $stock_value = $branch->findvalue('*[contains(name(),"stock")]');
$branch_stock{$branch_name} = $stock_value;
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论