解析出节点和属性XML :: LibXML

时间:2014-08-21 15:30:06

标签: xml perl xpath xml-libxml

我正在尝试搜索属性的值(xxxx01)并返回节点(0x100540)。这是我的xml:

  <model-response-list error="EndOfResults" throttle="86" total-models="86" xmlns="http://www.ca.com/spectrum/restful/schema/response">
   <model-responses>
      <model mh="0x100540">
         <attribute id="0x1006e">xxxx01</attribute>
      </model>
      <model mh="0x100c80">
         <attribute id="0x1006e">xxx02</attribute>
      </model>
</model-responses>
</model-response-list>

我在下面的代码中有一个var中的xml:

#Get restful req
  $client->setHost('http://wltsvpnms02.aigfpc.com:8080');
  $client->GET('/spectrum/restful/devices?attr=0x1006e', $headers) || die  "$!";
  my $parser     = XML::LibXML->new();
  my $xmldoc     = XML::LibXML->load_xml( string => $client->responseContent() )|| die "$!";

我已经尝试了每个xpath搜索,我可以找到一些文档(也许我只是无法理解它),但无法提出解决方案。

感谢您的帮助。

3 个答案:

答案 0 :(得分:1)

这似乎有效。

#!/usr/bin/perl

use warnings;
use strict;
use 5.010;

use XML::LibXML;

my $xml = '<model-response-list error="EndOfResults" throttle="86" total-models="86" xmlns="http://www.ca.com/spectrum/restful/schema/response">
   <model-responses>
      <model mh="0x100540">
         <attribute id="0x1006e">xxxx01</attribute>
      </model>
      <model mh="0x100c80">
         <attribute id="0x1006e">xxx02</attribute>
      </model>
</model-responses>
</model-response-list>';

my $xmldoc = XML::LibXML->load_xml( string => $xml );

my @nodes = $xmldoc->findnodes(q(//*[text()='xxxx01']/../@mh));

foreach (@nodes) {
  say $_->value;
}

我的XPath有点生疏。可能有更好的解决方案。

答案 1 :(得分:1)

罪魁祸首几乎可以肯定是

xmlns="http://www.ca.com/spectrum/restful/schema/response"

XPath中的未加前缀的元素名称是指不在命名空间中的元素,而文档中的所有元素都在http://www.ca.com/spectrum/restful/schema/response命名空间中,因此显而易见的路径如

//model[attribute = 'xxxx01']

会失败。您需要使用XPathContext来处理命名空间:

my $xc = XML::LibXML::XPathContext->new($xmldoc);
$xc->registerNs('resp', 'http://www.ca.com/spectrum/restful/schema/response');
my @nodes = $xc->findnodes('//resp:model[resp:attribute = "xxxx01"]');

使用您在XPath表达式中传递给registerNs的前缀。

答案 2 :(得分:1)

根据XML::LibXML的建议other question做出选择。

如果没有命名空间,您的目标可以像以下一样轻松解决:

my $mh = $xmldoc->findvalue('//model[attribute = "xxxx01"]/@mh');

然而,导航XML的更具挑战性的事情之一是命名空间,正如根节点的xmlns属性所指定的那样。

我可以推荐两种方法来解决这个问题:

  1. 在使用XML::LibXML::XPathContext查询之前注册命名空间。

    以下要求您事先知道名称空间URI。

    use XML::LibXML;
    use XML::LibXML::XPathContext;
    
    my $xmldoc = XML::LibXML->load_xml( string => $string);
    my $context = XML::LibXML::XPathContext->new( $xmldoc->documentElement() );
    $context->registerNs( 'u' => 'http://www.ca.com/spectrum/restful/schema/response' );
    
    my $mh = $context->findvalue('//u:model[u:attribute = "xxxx01"]/@mh');
    print "$mh\n";
    

    输出:

    0x100540
    

    但是,如果您不想对URI进行硬编码,也可以确定命名空间:

    my $ns = ( $xmldoc->documentElement()->getNamespaces() )[0]->getValue();
    $context->registerNs( 'u' => $ns );
    
  2. 使用local-name函数查询忽略名称空间:

    这会产生更长的XPath,但也需要更少的设置:

    use XML::LibXML;
    
    my $xmldoc = XML::LibXML->load_xml( string => $string);
    
    my $mh = $xmldoc->findvalue('//*[local-name() = "model"]/*[local-name() = "attribute"][text() = "xxxx01"]/../@mh');
    print "$mh\n";
    

    输出:

    0x100540
    
  3. 如果需要一点时间来吸收XPath语法和XML::LibXML的框架,请不要气馁。我认为名称空间是高级主题,甚至昨天我自己也问过a question

    幸运的是,没有多少学习曲线会占用您节省的时间,以避免XML::Simple会引入的错误。