如何使用 ends-with 访问以特定文本结尾的 XML 标签

huangapple go评论57阅读模式
英文:

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

&lt;?xml version=&quot;1.0&quot; ?&gt;
&lt;Details date=&quot;2022-02-09&quot; ver=&quot;1&quot;&gt;
&lt;VerNum&gt;/14&lt;/VerNum&gt;
&lt;Info&gt;
 &lt;model&gt;S22&lt;/model&gt;
 &lt;branch name=&quot;city_1&quot;&gt;
  &lt;prevstock&gt;10000&lt;/prevstock&gt;
  &lt;def&gt;1&lt;/def&gt;
 &lt;/branch&gt;
 &lt;branch name=&quot;city_2&quot;&gt;
  &lt;presstock&gt;2000&lt;/presstock&gt;
  &lt;def&gt;2&lt;/def&gt;
 &lt;/branch&gt;
 &lt;branch name=&quot;city_3&quot;&gt;
  &lt;futstock&gt;3000&lt;/futstock&gt;
  &lt;def&gt;0.3&lt;/def&gt;
 &lt;/branch&gt;
&lt;/Info&gt;
&lt;/Details&gt;

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 &#39;say&#39;;
use Data::Dumper;
use XML::LibXML;

my $file = &quot;ex.xml&quot;;#// die &quot;Usage: $0 filename\n&quot;;
my $parser = XML::LibXML-&gt;load_xml(location =&gt; $file);
my %branch_stock;
foreach my $sec ($parser-&gt;findnodes(&#39;/Details/Info&#39;)) { 
    for my $branch ($sec-&gt;findnodes(&#39;./branch&#39;)) {
        my $branch_name = $branch-&gt;getAttribute(&#39;name&#39;);
        my $stock_value = $branch-&gt;findnodes(&#39;*[ends-with(name(),&quot;stock&quot;)]&#39;)-&gt;[0]-&gt;textContent;

        #say &quot;$branch_name --&gt; $stock_value&quot;;

        $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(&quot;stock&quot;) + 1)  = &quot;stock&quot;]

So:

my $stock_value = $branch
-&gt;findnodes(&#39;node()[substring(local-name(),
    string-length(
        local-name()) - string-length(&quot;stock&quot;) + 1)  = &quot;stock&quot;]&#39;
)-&gt;[0]-&gt;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.


Related: https://stackoverflow.com/a/40935676/465183

¹ https://www.w3.org/TR/xpath-functions-31/#func-ends-with

答案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 &#39;say&#39;;

use Data::Dumper;
use XML::LibXML;

my $file = shift // die &quot;Usage: $0 file\n&quot;;

my $parser = XML::LibXML-&gt;load_xml(location =&gt; $file);

my %branch_stock;
for my $sec ($parser-&gt;findnodes(&#39;/Details/Info&#39;)) { 
    for my $branch ($sec-&gt;findnodes(&#39;./branch&#39;)) {
        my $branch_name = $branch-&gt;getAttribute(&#39;name&#39;);

        for my $stock ($branch-&gt;findnodes(&#39;*[contains(name(),&quot;stock&quot;)]&#39;)) {
            say &quot;$branch_name --&gt; $stock&quot;;
            $branch_stock{$branch_name} = $stock-&gt;textContent;

            # No &quot;ends-with&quot; in XPath1, what we have here
            # $branch-&gt;findnodes(&#39;*[ends-with(name(),&quot;stock&quot;)]&#39;)
        }
    }   
}
print Dumper \%branch_stock;

This prints

<!-- language: lang-none -->

city_1 --&gt; &lt;prevstock&gt;10000&lt;/prevstock&gt;
city_2 --&gt; &lt;presstock&gt;2000&lt;/presstock&gt;
city_3 --&gt; &lt;futstock&gt;3000&lt;/futstock&gt;
$VAR1 = {
          &#39;city_3&#39; =&gt; &#39;3000&#39;,
          &#39;city_1&#39; =&gt; &#39;10000&#39;,
          &#39;city_2&#39; =&gt; &#39;2000&#39;
        };

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-&gt;findnodes(&#39;/Details/Info&#39;)) { 
    for my $branch ($sec-&gt;findnodes(&#39;./branch&#39;)) {
        my $branch_name = $branch-&gt;getAttribute(&#39;name&#39;);

        my $stock = $branch-&gt;findnodes(&#39;*[contains(name(),&quot;stock&quot;)]&#39;)-&gt;[0];
        say &quot;$branch_name --&gt; $stock&quot;;
        $branch_stock{$branch_name} = $stock-&gt;textContent;
     }
}

And there is a shortcut for that, findvalue

for my $sec ($parser-&gt;findnodes(&#39;/Details/Info&#39;)) { 
    for my $branch ($sec-&gt;findnodes(&#39;./branch&#39;)) {
        my $branch_name = $branch-&gt;getAttribute(&#39;name&#39;);

        my $stock_value = $branch-&gt;findvalue(&#39;*[contains(name(),&quot;stock&quot;)]&#39;);
        $branch_stock{$branch_name} = $stock_value;
     }
}

huangapple
  • 本文由 发表于 2023年2月14日 02:33:01
  • 转载请务必保留本文链接:https://go.coder-hub.com/75439923.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定