views:

784

answers:

1

I am having trouble understanding / using name spaces with XML::LibXML package in Perl. I can access an element successfully but not an attribute. I have the following code which accesses an XML file (http://pastebin.com/f3fb9d1d0).

my $tree = $parser->parse_file($file); # parses the file contents into the new libXML object.
my $xpc = XML::LibXML::XPathContext->new($tree); 
$xpc->registerNs(microplateML => 'http://moleculardevices.com/microplateML');   

I then try and access an element called common-name and an attribute called name.

foreach my $camelid ($xpc->findnodes('//microplateML:species')) {
  my $latin_name = $camelid->findvalue('@name');   
  my $common_name = $camelid->findvalue('common-name');  
  print "$latin_name, $common_name" ;
}

But only the latin-name (@name) is printing out, the common-name is not. What am I doing wrong and how can I get the common-name to print out as well?

What does the @name do in this case? I presume it is an array, and that attributes should be put into an array as there can be more than one, but elements (like common-name) should not be because there should just be one?

I've been following the examples here: http://www.xml.com/pub/a/2001/11/14/xml-libxml.html and here: http://perl-xml.sourceforge.net/faq/#namespaces_xpath, and trying to get their example camel script working with my namespace, hence the weird namespace.

+2  A: 

Make sure you XML file is valid then use $node->getAttribute("someAttribute") to access attributes.

@name is a attribute name. You'd use it in findnodes() to specify elements with a given attribute set. Eg. a path like:

//camelids/species[@name="Camelus bactrianus"]/

Here is a simple/contrived example:

#!/usr/bin/perl -w
use XML::LibXML;

my $parser = XML::LibXML->new();
my $doc = $parser->parse_file('/Users/castle/Desktop/animal.xml');

my $xc = XML::LibXML::XPathContext->new( $doc->documentElement()  );
$xc->registerNs('ns', 'http://moleculardevices.com/microplateML');

my @n = $xc->findnodes('//ns:species');
foreach $nod (@n) {
    print "A: ".$nod->getAttribute("name")."\n";

    my @c = $xc->findnodes("./ns:common-name", $nod);
    foreach $cod (@c) {
        print "B: ".$cod->nodeName;
        print " = ";
        print $cod->getFirstChild()->getData()."\n";
    }
}

Output is:

perl ./xmltest.pl 
A: Camelus bactrianus
B: common-name = Bactrian Camel
Niels Castle
Thanks Niels, I got my original question the wrong way around, the latin-name (@name) was printing out fine, the common-name was not. Not sure if that changes your answer. I tried your example but I wasn't able to print anything out at all. The problem I'm having is more to do with the namespace I think. If I don't use the first two namespace lines (with $xpc in them) and just reference the elemtents like the examples here http://www.xml.com/pub/a/2001/11/14/xml-libxml.html then everything is fine, but when I include the namespace, it doesn't work, somehow I'm not getting the elementIwant
John
I removed the namespace and balanced the tags in the XML file you were referring to make the example run. I'll add the namespace to the example.
Niels Castle
I updated your XML file at pastebin http://pastebin.com/f3fb9d1d0
Niels Castle
Many thanks Niels, I'll give it a go.
John
Thanks again Neils, I think there was another error in the xml file, this line should have been first (<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>) I fixed it (http://pastebin.com/f63e6d7a3) and tried running your example script above (thanks for doing that) but I do not get any output at all from the script. Did you try to run it? It seems that this line is not working my @n = $xc->findnodes('*/species');Do you have any idea why it is not working? Thanks.
John
I re-asked this specific question and got the answer that I should use this: my @n = $xc->findnodes('//ns:species'); which now works. thanks again. http://stackoverflow.com/questions/2079435/whats-wrong-with-this-findnodes-statement-in-my-perls-script
John
Man, that was embarrassing. I had removed the namespace declaration from the XML file while poking around with pasteboard and forgot to add it again before running the Perl script. Your comment is of course correct. I added it to the example script. The XML on pasteboard is still unbalanced, you need to add a closing </camelids>. Cheers, mate!
Niels Castle