Perl XML::LibXML $node->findnodes($xpath) 找到它不应该找到的节点

2024-02-11

这是我遇到问题的一些代码,我处理一些 XML,并在 OO 类的方法中从文档中重复的几个节点中的每个节点提取一个元素。每个节点的子树中应该只有一个这样的元素,但我的代码获取所有元素,就好像它对整个文档进行操作一样。

因为我只期望获得 oine 元素,所以我只使用数组的第零个元素,这导致我的函数输出错误的值(并且对于文档中的所有项目都是相同的)

这是一些说明问题的简化代码

$ cat t4.pl
#!/usr/bin/perl
use strict;
use warnings;
use XML::LibXML;

my $xml = <<EndXML;
<Envelope>
  <Body>
    <Reply>
      <List>
        <Item>
          <Id>8b9a</Id>
          <Message>
            <Response>
              <Identifier>55D</Identifier>
            </Response>
          </Message>
        </Item>
        <Item>
          <Id>5350</Id>
          <Message>
            <Response>
              <Identifier>56D</Identifier>
            </Response>
          </Message>
        </Item>
      </List>
    </Reply>
  </Body>
</Envelope>
EndXML

my $foo = Foo->new();

my $parser = XML::LibXML->new();
my $doc    = $parser->parse_string( $xml );
my @list   = $doc->getElementsByTagName( 'Item' );

for my $item ( @list ) {

    my $id = get( $item, 'Id' );
    my @messages = $item->getElementsByLocalName( 'Message' );

    for my $message ( @messages ) {

        my @children = $message->getChildNodes();

        for my $child ( @children ) {

            my $name = $child->nodeName;

            if ( $name eq 'Response' ) {
                print "child is a Response\n";
                $foo->do( $child, $id );
            }
            elsif ( $name eq 'text' ) {

                # ignore whitespace between elements
            }
            else {
                print "child name is '$name'\n";
            }
        }    # child
    }    # Message
}    # Item

# ..............................................

sub get {
    my ( $node, $name ) = @_;

    my $value   = "(Element $name not found)";
    my @targets = $node->getElementsByTagName( $name );

    if ( @targets ) {
        my $target = $targets[0];
        $value = $target->textContent;
    }

    return $value;
}

# ..............................................

package Foo;

sub new {
    my $self = {};
    bless $self;
    return $self;
}

sub do {
    my $self = shift;
    my ( $node, $id ) = @_;

    print '-' x 70, "\n", ' ' x 12, $node->toString( 1 ), "\n", '-' x 70, "\n";

    my @identifiers = $node->findnodes( '//Identifier' );
    print "do() found ", scalar @identifiers, " Identifiers\n";

    print "$id, ", $identifiers[0]->textContent, "\n\n";
}

这是输出

$ perl t4.pl
child is a Response
----------------------------------------------------------------------
            <Response>
              <Identifier>55D</Identifier>
            </Response>
----------------------------------------------------------------------
do() found 2 Identifiers
8b9a, 55D

child is a Response
----------------------------------------------------------------------
            <Response>
              <Identifier>56D</Identifier>
            </Response>
----------------------------------------------------------------------
do() found 2 Identifiers
5350, 55D

我正期待着

do() found 1 Identifiers

我原以为最后一行是

5350, 56D

由于平台问题,我正在使用旧版本的 XML::LibXML。

Q:是后续版本出现这个问题还是我操作错误?


来自XPath 1.0 的文档 http://www.w3.org/TR/xpath/#path-abbrev

//para 选择所有 para 后代文档根目录

(强调我自己的)。所以你的电话

$node->findnodes( '//Identifier' )

忽略上下文节点$node并寻找所有Identifier文档中任意位置的元素

为了得到所有Identifier上下文节点的后代,您必须添加一个点,如下所示

$node->findnodes('.//Identifier');

但是由于$node总是一个Response元素和Identifier是的直接子代Response你可以写

$node->findnodes('Identifier');

你写这篇文章似乎有点紧张。我知道你已经删减了代码作为示例,但是你really需要单独的包吗?明智地应用 XPath 可以做很多事情。

最明显的变化是不需要循环遍历all孩子们 - 您可以简单地挑选出您感兴趣的孩子。

这段重构的代码可能值得一读

use strict;
use warnings;

use XML::LibXML;

my $parser = XML::LibXML->new;
my $doc    = $parser->parse_fh(*DATA);

for my $item ( $doc->findnodes('//Item') ) {

    print "\n";

    my ($id) = $item->findvalue('Id');
    printf "Item Id: %s\n", $item->findvalue('Id');

    my @messages = $item->findnodes('Message');

    for my $message (@messages) {
        my ($response) = $message->findnodes('Response');
        printf "Response Identifier: %s\n", $response->findvalue('Identifier');
    }
}

__DATA__
<Envelope>
  <Body>
    <Reply>
      <List>
        <Item>
          <Id>8b9a</Id>
          <Message>
            <Response>
              <Identifier>55D</Identifier>
            </Response>
          </Message>
        </Item>
        <Item>
          <Id>5350</Id>
          <Message>
            <Response>
              <Identifier>56D</Identifier>
            </Response>
          </Message>
        </Item>
      </List>
    </Reply>
  </Body>
</Envelope>

output

Item Id: 8b9a
Response Identifier: 55D

Item Id: 5350
Response Identifier: 56D
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Perl XML::LibXML $node->findnodes($xpath) 找到它不应该找到的节点 的相关文章

随机推荐

  • App Engine:NDB 和数据存储之间的区别

    我现在正在浏览 Google App Engine 文档 Python 发现了两种不同类型的存储 NDB 数据存储 数据库数据存储 两个配额限制 免费 似乎相同 它们的数据库设计也相同 然而NDB会自动将数据缓存在Memcache中 我实际
  • Quartz:不实现接口成员

    我正在使用 Quartz 并使用示例代码并收到错误 CS0738 EmailJob 未实现接口成员IJob Execute IJobExecutionContext EmailJob Execute IJobExecutionContext
  • 在 SignalR Hub 上使用通用方法

    我正在为我的 SignalR 服务器创建一个 Hub 类 并希望使用一种通用方法 这将使我免于编写大量代码 但是 当我尝试从 Xamarin iOS 客户端 也是 C 语言 调用下面的服务器代码时 SignalR 给了我错误 服务器代码 您
  • Java:ExecutorService 在达到一定队列大小后阻塞提交[重复]

    这个问题在这里已经有答案了 我正在尝试编写一个解决方案 其中单个线程生成可以并行执行的 I O 密集型任务 每个任务都有重要的内存数据 所以我希望能够限制当前待处理的任务数量 如果我像这样创建 ThreadPoolExecutor Thre
  • 给定三角形顶点坐标,求 3D 三角形的旋转角度

    我尝试在 3D 中旋转和平移一个等边三角形 直到他的顶点到达某个坐标 顶点坐标F G H and F G H 已知 我能够找到新的质心c 坐标是这样的 c x F x G x H x 3 c y F y G y H y 3 c z F z
  • Android 外部库项目出现 NoClassDefFoundError

    我使用 eclipse 进行 Google Android 开发 我创建了一个图书馆项目 x Is Library在 Android 设置中 其中包括外部 jar 文件 参考库 该库项目在另一个项目 将使用该库项目的实际项目 中引用 这是通
  • 错误 - 未标记为可序列化

    我收到的错误是 Type OrgPermission in Assembly App Code ptjvczom Version 0 0 0 0 Culture neutral PublicKeyToken null is not mark
  • 根据父子数据绘制树形图或组织图

    我在带有 GroupID TreeID 的表中有父子信息 从这张表中我想得出这样的结果 画树的目的只是为了观看 该表有数千个组ID 树结构 我正在使用 NET 平台 我应该如何进行 create table parent child Gro
  • XXX_* 输入生成的 *.pb.go 文件

    我正在研究一个tutorial https ewanvalentine io microservices in golang part 1 关于 gRPC 当我生成 pb go文件 我得到一些XXX 输入我的结构 这是我的consignme
  • Eclipse > Javascript > 代码高亮不能使用对象表示法

    我在用Eclipse Helios 使用 PDT 以及当我使用默认值编辑 JavaScript 文件时JavaScript Editor JSDT 代码高亮 Mark Occurrences 不适用于 JSON style or Objec
  • 嵌套 vue.js 实例/元素

    这可能听起来像一个真正的菜鸟问题 但我对 MVVM 甚至 JS 中的 MVC 都很陌生 所以提前抱歉 我正在使用 vue js 并且到目前为止很喜欢它的简单性 但对于我想做的事情 我认为我需要以不同的方式去做 我想将 Vue 实例 元素嵌套
  • 从两个多态类继承

    给出以下代码 class T public virtual T virtual void foo 0 class U public U U void bar std cout lt lt bar lt lt std endl class A
  • 如何在 join linq 语法中比较 null

    处理 EF 4 C Face join 中的问题 SQL语法 Select a Code b Name from DepartmentMaster a Join DepartmentDetail b on isnull a ID 0 isn
  • 如何在 Snakemake 表格配置中使用列表,用于描述生物信息学管道的测序单元

    如何在 Snakemake 表格配置中使用列表 我使用 Snakemake Tabular 与 BWA mem 映射 配置来描述我的测序单元 在单独的行上测序的文库 在分析的下一阶段 我必须合并测序单元 映射的 bed 文件 并获取合并的
  • 使用 PORT=XXXX 启动节点永久脚本

    当通过特定端口运行节点命令时 我会这样启动应用程序 PORT 1234 node app js 我如何将端口传递给forever命令 无论我做什么 似乎都不想工作 我试过了 将端口作为参数传递 forever start app js 12
  • 循环浏览用户指定的根目录中的子文件夹和文件[重复]

    这个问题在这里已经有答案了 我通过各个文件的循环脚本工作正常 但我现在还需要它来查找多个目录 我被困住了 事情发生的顺序 提示用户选择他们需要的根目录 我需要脚本来查找该根目录中的任何文件夹 如果脚本找到一个 它将打开第一个 所有文件夹 因
  • 如何在Java中使用Graphics2D旋转文本?

    我想使用 Graphics2D 旋转 JPanel 上的文本 我的代码是这样的 double paso d width numeroBarras double alto datos i valor Font fBarras new Font
  • 在 MATLAB 中实现显式欧拉方法(适用于 ODE)

    我到处都找过了 但什么也没找到 首先 我想说我从未使用过 Mat Lab 所以我不知道我在做什么 我尝试了一些方法 但没有任何效果 显然 y 0 2 试图创建一个包含 0 个值为 2 的单位的列表 无论如何 有人可以帮助我吗 我需要在 Ma
  • 我们可以在透明窗口上应用着色器吗

    I am looking to apply a particular shader to a transparent window for example on a live desktop I want to create a trans
  • Perl XML::LibXML $node->findnodes($xpath) 找到它不应该找到的节点

    这是我遇到问题的一些代码 我处理一些 XML 并在 OO 类的方法中从文档中重复的几个节点中的每个节点提取一个元素 每个节点的子树中应该只有一个这样的元素 但我的代码获取所有元素 就好像它对整个文档进行操作一样 因为我只期望获得 oine