【问题标题】:Fixing of an XPath predicate for use in XML::Twig修复用于 XML::Twig 的 XPath 谓词
【发布时间】:2011-07-07 21:38:32
【问题描述】:

我正在尝试在 Perl 中编写一个子例程,当提供某些子节点的文本值时,它将删除 XML 中的给定节点。

给定 XML 喜欢:

<Path>
  <To>
    <My>
      <Node>
        <ChildA>ValA</ChildA>
        <ChildB>ValB</ChildB>
        <ChildC>ValC</ChildC>
      </Node>
    </My>
  </To>
</Path>
<!-- A lot of siblings follow... -->

我使用的 XPath 表达式本质上是:

/Path/To/My/Node[ChildA="ValA" and ChildB="ValB" and ChildC="ValC"]

当我尝试运行我的脚本时,我收到如下错误:

Error in XPath expression
/Path/To/My/Node[ChildA="ValA" and ChildB="ValB" and ChildC="ValC"] at 
ChildA="ValA" and ChildB="ValB" and ChildC="ValC" at Twig.pm line 3353

我对此感到茫然,正在寻找建议。我试过用谷歌搜索,但在XML::Twig 中找不到尝试使用这样的谓词的工作示例。我不知道问题出在我的 XPath 语法还是我如何使用XML::Twig

为了更好的衡量,我也尝试过:

/Path/To/My/Node[ChildA/text()="ValA" and ChildB/text()="ValB" and ChildC/text()="ValC"]

也没有运气。解决办法是什么?

【问题讨论】:

  • 您是将此表达式与findnodes 一起使用还是作为twig_handlers 的触发器? findnodes,正如 Sean 所提到的,将允许您使用所有 XPath,前提是您使用 XML::Twig::XPath。触发处理程序的谓词受到更多限制,您需要为Node 提供一个处理程序来测试子项的值。

标签: perl xpath xml-twig


【解决方案1】:

在测试中,Node是上下文节点,所以不得不说:

/Path/To/My/Node[./ChildA="ValA" and ./ChildB="ValB" and ./ChildC="ValC"]

这适用于我使用XML::XPath 的简短测试程序。

编辑:抱歉,我对 XML::Twig 不太熟悉,而且我对它的 XPath 功能做出了错误的假设。根据文档,它仅支持“类似 XPath”的语法,不会上升到您的示例的复杂程度。但是,如果您使用 XML::Twig::XPath 而不是 XML::Twig,您将获得完整的 XPath 引擎:

my $twig = XML::Twig::XPath->new;
$twig->parse('your string');
my $nodes = $twig->findnodes('/Path/To/My/Node[ChildA="ValA" and ChildB="ValB" and ChildC="ValC"]');
print $nodes;

这将打印“ValAValBValC”。

【讨论】:

    【解决方案2】:

    有两种方法可以做到这一点:通过加载整个 XML 并删除不需要的节点,然后输出树枝,或者在进行时进行过滤,这有点复杂,但使用的内存更少。

    第一种方式(你可能需要最新版本的 XML::XPathEngine,我没有用旧版本或 XML::XPath 测试过,它也可以充当 XPath 引擎)

    #!/usr/bin/perl
    
    use strict;
    use warnings;
    
    use XML::Twig::XPath;
    
    my $t= XML::Twig::XPath->new( pretty_print => 'indented')
                           ->parse( \*DATA);
    $_->delete for ($t->findnodes( '/Path/To/My/Node[./ChildA="ValA" and ./ChildB="ValB" and ./ChildC="ValC"]'));
    
    $t->print;
    
    __DATA__
    <Path>
      <To>
        <My>
          <Node>
            <ChildA>ValA</ChildA>
            <ChildB>ValB</ChildB>
            <ChildC>ValC</ChildC>
          </Node>
          <Node>
            <ChildA>ValD</ChildA>
            <ChildB>ValB</ChildB>
            <ChildC>ValC</ChildC>
          </Node>
        </My>
      </To>
    </Path>
    

    以及“过滤”方式:

    #!/usr/bin/perl
    
    use strict;
    use warnings;
    
    use XML::Twig;
    
    XML::Twig->new( twig_roots => { '/Path/To/My/Node' => \&filter },
                    twig_print_outside_roots => 1,
                    keep_spaces => 1,
                  )
             ->parse( \*DATA);
    exit;
    
    # the handler expressions cannot lookahead, so we need to look at each node
    # once it's completely parsed
    sub filter
      { my( $t, $node)= @_;
        if(    ($node->field( 'ChildA') eq 'ValA')
            && ($node->field( 'ChildB') eq 'ValB')
            && ($node->field( 'ChildC') eq 'ValC')
          )
          { $node->delete; }
        else
          { $t->flush; }
      }
    
    __DATA__
    <Path>
      <To>
        <My>
          <Node>
            <ChildA>ValA</ChildA>
            <ChildB>ValB</ChildB>
            <ChildC>ValC</ChildC>
          </Node>
          <Node>
            <ChildA>ValD</ChildA>
            <ChildB>ValB</ChildB>
            <ChildC>ValC</ChildC>
          </Node>
        </My>
      </To>
    </Path>
    

    【讨论】:

      猜你喜欢
      • 2018-04-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-10-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多