Beefy Boxes and Bandwidth Generously Provided by pair Networks
Problems? Is your data what you think it is?

XML namespace question

by zhubin (Initiate)
on Oct 07, 2015 at 23:34 UTC ( #1144119=perlquestion: print w/replies, xml ) Need Help??

zhubin has asked for the wisdom of the Perl Monks concerning the following question:

Hi all, I'm trying to parse a netconf response from a Juniper switch using XML::LibXML and the XML file/content looks like this:

<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns:junos +=""> <vlan-information junos:style="terse"> <vlan-terse/> <vlan> ... </vlan> </vlan-information> </rpc-reply>

and my simple attempt to parse out some info looks like this:

my $jparser = XML::LibXML->new; my $doc = $jparser->parse_string($jnx->{'server_response'}); my @vlans = $doc->findnodes("/rpc-reply/vlan-information/vlan"); foreach my $vlan (@vlans){ my @parameters = $vlan->findnodes(" ./vlan-name| ./vlan-tag| .//vlan-member-interface|"); foreach my $param (@parameters){ print $param->localname, " : ", $param->to_literal, "\n"; } }

What I'm seeing is, I get no output.. and my troubleshooting led me to believe its something to do with the namespace details in the first line of the XML... and sure enough, if I remove the xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" in the first line, everything works fine. Any thoughts on how I should be calling the LibXML parser in order for this data to parse as is (I don't want to have to modify all the responses my code receives by stripping out the xmlns= portion) or am I just missing something here. Much thanks!

Replies are listed 'Best First'.
Re: XML namespace question
by tangent (Vicar) on Oct 08, 2015 at 00:49 UTC
    tobyink provides another solution here Re: LibXML, XPath and Namespaces
    my $string = q| <rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns:junos +=""> <vlan-information junos:style="terse"> <vlan> <vlan-name>Vlan</vlan-name> </vlan> </vlan-information> </rpc-reply>|; my $jparser = XML::LibXML->new; my $doc = $jparser->parse_string($string); my @vlans = $doc->findnodes("//vlan-information/vlan"); print "found ", scalar @vlans, " vlans\n"; print "The root element's namespace is: ", $doc->documentElement->namespaceURI, "\n"; # Give that namespace a prefix so that we can reference it in XPath $doc->documentElement->setNamespaceDeclPrefix("", "x"); @vlans = $doc->findnodes("//x:vlan-information/x:vlan"); print "found ", scalar @vlans, " vlans\n"; foreach my $vlan (@vlans){ my @parameters = $vlan->findnodes("./x:vlan-name"); print "found ", scalar @parameters, " parameters\n"; }
    found 0 vlans The root element's namespace is: urn:ietf:params:xml:ns:netconf:base:1 +.0 found 1 vlans found 1 parameters
    A reply falls below the community's threshold of quality. You may see it by logging in.
Re: XML namespace question
by Anonymous Monk on Oct 07, 2015 at 23:52 UTC
Re: XML namespace question
by ikegami (Pope) on Oct 08, 2015 at 17:48 UTC

    Create a context and assign prefixes to the namespaces. The prefixes you choose need not match those used in the document, if any.

    use XML::LibXML::XPathContext qw( ); my $xpc = XML::LibXML::XPathContext->new(); $xpc->registerNs('nc', 'urn:ietf:params:xml:ns:netconf:base:1.0'); $xpc->registerNs('j', '');

    You don't appear to be using the second namespace in your queries. If it's the case, you don't need to register a prefix for it.

    Then change $node->find*($xpath) to $xpc->find*($xpath, $node), and use the defined prefixes to specify the namespace of the elements you want to locate.

    $xpc->findnodes( "/nc:rpc-reply/nc:vlan-information/nc:vlan", $doc) $xpc->findnodes( "nc:vlan-name|nc:vlan-tag|.//nc:vlan-member-interface", $vlan)

    (Removed needless instances of "./".)

Re: XML namespace question
by perlific (Initiate) on Oct 13, 2015 at 23:30 UTC

    Thanks everyone for your insightful (and entertaining?) comments - yes, this is the OP, but being new I may have contravened best practice for changing my username (my apologies to the gods, and other fellow monks). The suggested solutions were all very helpful in understanding the nuances of namespaces (no namespace vs. default namespace and so on) and the multiple ways to come at this sort of problem. I have a further question now with regard to attributes. Whats the best method to pull out the value of a given attribute (like junos:style="terse" in the vlan-information element), especially when the attribute is in a different namespace as the containing element. Using the same XML snippet as in the original post:

    my $dom = XML::LibXML->load_xml( string => $jnx->{'server_response'}, # parser options ... ); my $xpc = XML::LibXML::XPathContext->new(); $xpc->registerNs('nc', 'urn:ietf:params:xml:ns:netconf:base:1.0'); $xpc->registerNs('j', ''); my @nodes = $xpc->findnodes( "/nc:rpc-reply/nc:vlan-information", $dom +); foreach my $node (@nodes){ print "\nAttempt 1\n"; my $attr_style = $node->findvalue('/@style'); $attr_style ? print "Got Attr _style_: $attr_style\n" : print "Cou +ldn't find Attr _style_\n"; print "\nAttempt 2\n"; my $test1 = $node->hasAttributes(); print " Yes I have attribute(s)\n" if $test1; print "\nAttempt 3\n"; my @attrs = $node->attributes('/style'); foreach my $attr (@attrs){ print "here's an attribute: $attr->to_literal\n"; } print "\nAttempt 4\n"; my $attrnode = $node->getAttributeNodeNS( ' +junos/12.3R9/junos', 'style'); print "here's an attribute: $attrnode\n"; }

    My attempts above (the last two at least) yield a result but its not ideal... I want to say for attribute "style" in namespace "junos", what is the value? Thanks!

      #!/usr/bin/perl -- use strict; use warnings; use Data::Dump qw/ dd /; use XML::LibXML 1.70; ## for load_html/load_xml/location sub XML::LibXML::Node::F { my $self = shift; my $xpath = shift; my %prefix = @_; our $XPATHCONTEXT; $XPATHCONTEXT ||= XML::LibXML::XPathContext->new(); while( my( $p, $u ) = each %prefix ){ $XPATHCONTEXT->registerNs( $p, $u ); } $XPATHCONTEXT->findnodes( $xpath, $self ); } my $doc = XML::LibXML->new()->parse_string(q{<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns:junos=""> <vlan-information junos:style="terse"> <vlan-terse/> <vlan> ... </vlan> </vlan-information> </rpc-reply> }); ## "registration" find a node that don't exist $doc->F( "/NOTExIST", 'nc', 'urn:ietf:params:xml:ns:netconf:base:1.0', 'j', '', ); for my $vlaninfo( $doc->F( "/nc:rpc-reply/nc:vlan-information", ) ){ my( $style ) = $vlaninfo->F('./@j:style'); print "$style\n"; dd( { %$vlaninfo }); } __END__ junos:style="terse" { "{}style" => "terse" }
A reply falls below the community's threshold of quality. You may see it by logging in.
A reply falls below the community's threshold of quality. You may see it by logging in.
A reply falls below the community's threshold of quality. You may see it by logging in.

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1144119]
Approved by Old_Gray_Bear
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others rifling through the Monastery: (4)
As of 2020-02-21 03:11 GMT
Find Nodes?
    Voting Booth?
    What numbers are you going to focus on primarily in 2020?

    Results (93 votes). Check out past polls.