【问题标题】:How to select siblings (xpath syntax) with Perl's XML::Twig?如何使用 Perl 的 XML::Twig 选择兄弟姐妹(xpath 语法)?
【发布时间】:2012-07-05 07:56:24
【问题描述】:

我需要通过 next_siblingfirst_elt 选择下一个节点。但我想按节点名称过滤(包含字符串 "TON"

first_elt ('HILTON[@method]' or 'SHERATON[@method]');

next_sibling ('HILTON[@method]' or 'SHERATON[@method]');

next_sibling ('TON[@method]');

我试过的例子(不工作):

#!/usr/bin/perl -w
use warnings;
use XML::Twig;
$t-> parsefile ('file.xml');

my $Y0=$t->first_elt('HILTON[@method]' or 'SHERATON[@method]');

它只会处理 'HILTON[@method]'

my $Y0=$t->first_elt('/*TON[@method]');

C:/strawberry/perl/site/lib/XML/Twig.pm 第 3523 行的错误导航条件 '/*TON[@method]' ()

【问题讨论】:

    标签: perl xpath xml-parsing twig xml-twig


    【解决方案1】:

    由于这超出了 XML::Twig 支持的 XPath 子集,您必须使用自定义过滤器,将代码传递给 first_elt:

    $t->first_elt( sub {  $_[0]->tag=~ m{TON$} && $_[0]->att( 'method') })
    

    这将返回第一个子元素返回真值的元素。

    不过,需要这样的表达方式有点令人不安。在您的示例中,您通过其名称以 TON 结尾的事实来定义一类元素。当你有一个 CARLTON 元素时会发生什么?或者当 MARRIOTT 元素需要与 SHERATON 和 HILTON 一起处理时?您需要重写查询吗?

    如果您是设计数据格式的人,我建议您修改格式。 HILTON 和 SHERATON 应该是 HOTEL、BRAND 或 OWNER 标签的属性。也许一个额外的属性会很有用,以标记这两种类型应该被类似地处理。只有当它是数据固有的属性时,此属性才有意义。

    如果数据是真实的并且您没有输入其格式,那么我将有一个标签列表来处理并检查这些:

    my %TAGS_TO_PROCESS= map { $_ => 1 } qw( HILTON SHERATON);
    my $elt= $t->first_elt( sub {  $TAGS_TO_PROCESS{$_[0]->tag} && $_[0]->att( 'method') })
    

    这样添加/减去其他标签很容易。

    【讨论】:

    • 是的,数据已提供且无法更改。实际上 HILTON、SHERATON 只是虚拟名称,因为我无法发布原始数据。我目前正在尝试您的解决方案,看起来很棒
    【解决方案2】:

    使用

    *[substring(name(), string-length(name()) - 2) = 'TON'][@method][1]
    

    说明

    此表达式使用 XPath 2.0 标准函数 ends-with() 的 XPath 1.0 等效项:

    XPath 2.0 表达式的 XPath 1.0 等效项:

    ends-with($s, $s2)
    

    substring($s, string-lenth() - string-length($s2) + 1) = $s2
    

    在最后一个表达式中,我们将$s 替换为name(),将$s2 替换为'TON'

    基于 XSLT 的验证

    <xsl:stylesheet version="1.0"
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
     <xsl:output omit-xml-declaration="yes" indent="yes"/>
     <xsl:strip-space elements="*"/>
    
     <xsl:template match="/*">
         <xsl:copy-of select=
         "*[substring(name(), string-length(name()) - 2) = 'TON'][@method] "/>
    ==========  
         <xsl:copy-of select=
         "*[substring(name(), string-length(name()) - 2) = 'TON'][@method][1] "/>
     </xsl:template>
    </xsl:stylesheet>
    

    应用于此 XML 文档时:

    <t>
     <HILTON method="buy"/>
     <TON method="burn"/>
     <TONIC method="drink"/>
     <HILTON nomethod="yes"/>
     <SHERATON/>
     <SHERATON method="visit"/>
    </t>
    

    转换计算两个 XPath 表达式并将选定节点复制到输出

    <HILTON method="buy"/>
    <TON method="burn"/>
    <SHERATON method="visit"/>
    ==========  
         <HILTON method="buy"/>
    

    第一个表达式选择所有元素 - 上下文节点的子节点,其名称以“TON”结尾并且还具有 method 属性。

    第二个表达式从第一个表达式选择的节点中选择第一个节点。

    【讨论】:

    • 非常感谢 Xpath 1.0 的回答。不幸的是,Perl::Twig 方法 (first_elt) 似乎不理解这个高级 Xpath 请求。 $t-&gt; parsefile ('WO-tampon.xwo'); my $Y0=$t-&gt;first_elt('*[substring(name(), string-length(name()) - 2) = "TON"][@method][1]'); 错误的导航条件 '*[substring(name(), string-length(name()) - 2) = "TON"][@method][1]' () at C:/strawberry/ perl/site/lib/XML/Twig.pm 第 3523 行
    • 我将使用您的 xpath 请求尝试另一个应该支持 Xpath 1.0 的 Perl::Twig 方法:getNextSibling。将发布结果
    • 所有导航方法都使用相同的底层“引擎”,它不支持所有 XPath 1.0。盒子上也没有这么说。如果您需要 XPath 支持,请使用 XML::Twig::XPath,然后findnodes 将为您提供完整的 XPath 1.0 支持。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多