【问题标题】:parse out node and attribute XML::LibXML解析出节点和属性 XML::LibXML
【发布时间】:2014-08-22 15:58:23
【问题描述】:

我正在尝试搜索属性的值 (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 搜索,我可以找到一些文档(也许我只是无法理解它),但无法提出解决方案。

感谢您的帮助。

【问题讨论】:

    标签: xml perl xpath xml-libxml


    【解决方案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 有点生锈了。可能有更好的解决方案。

    【讨论】:

    • 确实如此!非常感谢!
    【解决方案2】:

    罪魁祸首几乎可以肯定

    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 的前缀。

    【讨论】:

      【解决方案3】:

      根据other question 上的建议,XML::LibXML 上的不错选择。

      如果没有命名空间,您的目标可以很容易地解决:

      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
        

      如果花一点时间来吸收XPath 语法和XML::LibXML 的框架,不要气馁。我认为命名空间是高级主题,昨天我什至问过a question

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-12-06
        • 1970-01-01
        • 2013-03-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多