这里有一些我有问题的代码,我处理一些
XML,在一个OO类中的方法中,我从文档中重复的几个节点中的每一个中提取一个元素.在每个节点的子树中只应该有一个这样的元素,但是我的代码就像整个文档一样运行所有的元素.
因为我只希望得到元素我只使用数组的第零个元素,这导致我的函数输出错误的值(它对文档中的所有项都是一样的)
这是一些简化的代码,说明了这个问题
$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 ),'-' 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.
问:在以后的版本中是否存在问题,或者我做错了?
解决方法
从
documentation of XPath 1.0
//para selects all the para descendants of the document root
(强调我自己).所以你的电话
$node->findnodes( '//Identifier' )
忽略上下文节点$node并在文档中的任何位置搜索所有标识符元素
$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