以下是我遇到问题的一些代码,我处理了一些XML,在OO类的方法中,我从文档中重复的几个节点中提取了一个元素。每个节点的子树中应该只有一个这样的元素,但是我的代码获取所有元素,就好像它在整个文档上运行一样。
因为我只希望获得oine元素我只使用数组的第0个元素,这导致我的函数输出错误的值(对于文档中的所有项都是相同的)
这是一些说明问题的简化代码
$ 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。
问:以后的版本中是否存在问题,或者我做错了什么?
答案 0 :(得分:20)
// para选择 文档根目录的所有para后代
(强调我自己)。所以你的电话
$node->findnodes( '//Identifier' )
忽略上下文节点$node
并在文档中的任何位置搜索所有Identifier
元素
要获取上下文节点的所有Identifier
后代,您必须添加一个点,如下所示
$node->findnodes('.//Identifier');
但由于$node
始终是Response
元素且Identifier
是Response
的直接子项,因此您只需编写
$node->findnodes('Identifier');
你似乎已经把自己弄得有点紧张了。我知道你已经删除了代码作为一个例子,但你真的需要单独的包吗?通过明智地应用XPath可以做很多事情。
最明显的变化是你不需要遍历所有孩子 - 你可以简单地选出你感兴趣的孩子。
这个重构的代码可能值得一读
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>
<强>输出强>
Item Id: 8b9a
Response Identifier: 55D
Item Id: 5350
Response Identifier: 56D
答案 1 :(得分:0)
我对代码的质量没有评论,但在我使用XML::DOM
之前学会了使用XML::LibXML
我倾向于使用一些DOM语法。我一直在努力摆脱这种习惯:)
我提到这个的原因是因为我看到你使用等价的->item(0)
来获取节点列表中的第一个位置,就像在DOM中一样。
XML::LibXML
支持使用->item()
,但是从cpan我可以看到xpath创建的节点列表从1
开始,而不是0
,就像DOM一样。我很确定如果你按原样保留代码并查找第一个数组位置而不是第0个,你将得到你想要的结果。
不明确的是为什么->item(0)
给你最后的结果,因为它似乎从我的测试中得到(它可能是从数组值偏移,所以你实际上返回第-1个数组值)