Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Perl and LibXML to obtain sub node values when namespace is used

Tags:

perl

libxml2

I have the following XML as an example:

<root xmlns="http://www.plmxml.org/Schemas/PLMXMLSchema" >
    <parentNode status="Good">
        <A>
            <B>
                <C id="123" >C Node Value Here</C>
            </B>
        </A>
    </parentNode>
</root>

There are multiple parentNode nodes in my XML file (only one shown here), so I am cycling through parentNode's. Once I have one, I want to obtain attribute values 3 more levels down in the XML. My XML uses a name space and I have registed the name space in my Perl script as "plm". I can obtain the parentNode attribute value just fine using name space in my path. But when I try to navigate down to node "C" and pickup attribute "id", I am getting the following error:

XPath error : Undefined namespace prefix error : xmlXPathCompiledEval: evaluation failed

I am using the following Perl script.

use XML::LibXML;
use XML::LibXML::XPathContext;

my $filename = "namespaceissue.xml";

my $parser = XML::LibXML->new();
my $doc    = $parser->parse_file($filename);
my $xc = XML::LibXML::XPathContext->new( $doc->documentElement()  );
$xc->registerNs('plm', 'http://www.plmxml.org/Schemas/PLMXMLSchema'); 

foreach my $node ($xc->findnodes('/plm:root/plm:parentNode')) {
    my $status  = $node->findvalue('./@status');
    print "Status = $status\n";

    my $val = $node->findvalue('./plm:A/plm:B/plm:C/@title');
    print "Value = $val\n";

}

If I use no namespace on the sub-nodes ./A/B/C, the script continues with no error, but no value is assigned to $val. If I add the plm: prefix I get the namespace error. Does anybody know what I am doing wrong here? Do I have to use findnodes to first find the subnodes and then extract the value with findvalue? I tried that as well and did not have any luck.

like image 884
Brian Avatar asked Jul 02 '15 19:07

Brian


1 Answers

$node->findvalue('./plm:A/plm:B/plm:C/@title')

should be

$xc->findvalue('./plm:A/plm:B/plm:C/@id', $node)

Tips:

  • Those leading ./ are useless.

    $node->findvalue('./@status')
    
    $xc->findvalue('./plm:A/plm:B/plm:C/@id', $node)
    

    are the same as

    $node->findvalue('@status')
    
    $xc->findvalue('plm:A/plm:B/plm:C/@id', $node)
    
  • You can use getAttribute to get an element's attribute, so

    $node->findvalue('@status')
    

    can also be accomplished more efficiently using

    $node->getAttribute('status')
    
like image 168
ikegami Avatar answered Oct 09 '22 00:10

ikegami