1

I am trying to search for a value (xxxx01) of an attribute and return the node (0x100540) . Here is 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>

I have the xml in an var in the code below:

#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 "$!";

I have tried every xpath search i could find some documentation ( maybe i just cannot get my head around it) on but cannot come up with a solution.

Thanks for any help.

rj reilly
  • 29
  • 5

3 Answers3

1

This seems to work.

#!/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;
}

My XPath is a bit rusty. There might be a better solution.

Miller
  • 34,962
  • 4
  • 39
  • 60
Dave Cross
  • 68,119
  • 3
  • 51
  • 97
1

The culprit is almost certainly the

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

Unprefixed element names in XPath refer to elements that are not in a namespace, whereas all the elements in your document are in the http://www.ca.com/spectrum/restful/schema/response namespace, so the obvious paths like

//model[attribute = 'xxxx01']

will fail. You need to use an XPathContext to handle the namespace:

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"]');

using the prefix you passed to registerNs in the XPath expression.

Ian Roberts
  • 120,891
  • 16
  • 170
  • 183
1

Good choice on XML::LibXML per advice on your other question.

If there wasn't a namespace, your goal could be solved as easily as:

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

However, one of the more challenging things to navigate XML wise are namespaces, as is specified by the xmlns attribute of the root node.

There are two approaches that I can recommend for solving this:

  1. Register the namespace before querying using XML::LibXML::XPathContext.

    The following requires that you know the namespace URI in advance.

    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";
    

    Outputs:

    0x100540
    

    However, it's also possible to determine the namespace if you don't want to hardcode the URI:

    my $ns = ( $xmldoc->documentElement()->getNamespaces() )[0]->getValue();
    $context->registerNs( 'u' => $ns );
    
  2. Use the local-name function to query ignoring namespaces:

    This makes for a longer XPath, but also takes less setup:

    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";
    

    Outputs:

    0x100540
    

Don't feel discouraged if it takes a little while to absorb the XPath syntax and the framework of XML::LibXML. I consider namespaces advanced topics, and even had a ask a question with regard to them myself yesterday.

Fortunately, no amount of learning curve would take up the amount of time that you'll save avoiding bugs that XML::Simple would introduce.

Community
  • 1
  • 1
Miller
  • 34,962
  • 4
  • 39
  • 60