修复了在 XML::Twig 中使用的 XPath 谓词

发布于 2024-11-18 21:32:56 字数 1044 浏览 2 评论 0原文

我正在尝试用 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"]

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

I'm trying to write a subroutine in Perl that will delete a given node in XML when provided with the text values of some of the children nodes.

Given XML like:

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

The XPath expression I'm using is essentially:

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

When I'm trying to run my script, I'm getting an error like:

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

I'm at a loss for this and am looking for suggestions. I've tried googling around, but I can't find working examples of trying to use predicates like this in XML::Twig. I don't know if the problem is in my XPath syntax or how I'm using XML::Twig.

For good measure, I've also tried:

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

No luck with that either. What is the solution?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

隔岸观火 2024-11-25 21:32:56

在测试中,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”。

Within the test, Node is the context node, so you have to say:

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

This works for me in a short test program that uses XML::XPath.

EDIT: Sorry, I'm not so familiar with XML::Twig, and I made an incorrect assumption about its XPath capabilities. According to the documentation, it supports only an "XPath-like" syntax that doesn't rise to the level of complexity of your example. However, if you use XML::Twig::XPath instead of XML::Twig, you get the full XPath engine:

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;

This prints "ValAValBValC".

ゝ偶尔ゞ 2024-11-25 21:32:56

有两种方法可以做到这一点:加载整个 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>

There are 2 ways to do this: by loading the whole XML and deleting the nodes you don't want, then outputting the twig, or by filtering as you go along, which is a little more complex but uses less memory.

The first way (you may need a recent version of XML::XPathEngine, I haven't tested it with older ones or with XML::XPath, which can also act as the XPath engine)

#!/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>

And the "filter" way:

#!/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>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文