Retrieve attribute value from xml with namespaces using xmllint

xmlxmllint

I want to retrieve attribute value, using xpath option (without using xmllint' shell).

my xml:

<?xml version="1.0" encoding="UTF-8"?>
<XML>
    <hhi:Elem xmlns:hhi="http://www.w3.org/TR/html4/" version="1.0" sn="101"> 
    </hhi:Elem>
</XML>

this what I tried:

xmllint --xpath '//XML/*[local-name()='Elem']/@sn' FILE_1.xml
XPath set is empty

xmllint --xpath '//XML/*[local-name()='Elem' and namespace-uri()="http://www.w3.org/TR/html4/"]/@sn' FILE_1.xml
XPath set is empty

(I know I can use sed to strip away namespaces but I want to avoid that either)

Best Answer

Yes, don't use sed on XML. It's bad news.

I think your problem is actually a simple matter of quoting - you quote your xpath in single quotes, and Elem is also in single quotes.

I don't have a copy of xmllint handy, but this works:

#!/usr/env/perl

use strict;
use warnings;

use XML::LibXML;

my $xml = XML::LibXML -> load_xml ( IO => \*DATA ); 
print $xml -> findnodes('//XML/*[local-name()="Elem"]');

__DATA__
<?xml version="1.0" encoding="UTF-8"?>
<XML>
    <hhi:Elem xmlns:hhi="http://www.w3.org/TR/html4/" version="1.0" sn="101"> 
    </hhi:Elem>
</XML>

That prints:

sn="101"

You don't need the XML either, because // denotes "anywhere in tree". So you could use:

//*[local-name()="Elem"]/@sn
/XML/*[local-name()="Elem"]/@sn

For the sake of completeness, with registering the namespace you could do it like this:

#!/usr/env/perl

use strict;
use warnings;

use XML::LibXML;

my $xml = XML::LibXML -> load_xml ( IO => \*DATA ); 
my $xpc = XML::LibXML::XPathContext -> new;
$xpc -> registerNs('x', 'http://www.w3.org/TR/html4/');
print $xpc -> findnodes('/XML/x:Elem/@sn', $xml);

__DATA__
<?xml version="1.0" encoding="UTF-8"?>
<XML>
    <hhi:Elem xmlns:hhi="http://www.w3.org/TR/html4/" version="1.0" sn="101"> 
    </hhi:Elem>
</XML>

Now, I know this doesn't directly help with xmllint, aside from allowing testing of the xpath you're using. But I think it likely that it's quotes tripping you up, not the xpath.

Can I suggest you need:

xmllint --xpath '//*[local-name()="Elem"]/@sn' FILE_1.xml
Related Question