XML::LibXML braindead, or is it just me?
I spent the last 30 minutes chasing down this bug. The following tests should all pass but the last one doesn't:
use strict;
use warnings;
use Test::More 'no_plan';
use XML::LibXML;
use XML::LibXML::XPathContext;
my $parser = XML::LibXML->new;
ok($parser, 'XML::LibXML parser created');
my $xml = '<x:me xmlns:x="some_namespace" />';
my $xdoc = $parser->parse_string($xml);
ok($xdoc, 'Valid XML parsed');
$xdoc = XML::LibXML::XPathContext->new($xdoc);
ok($xdoc, 'Converted to XPathContext');
my ($node) = $xdoc->findnodes('/me');
ok(!$node, 'Not found because no prefix means NULL namespace');
($node) = $xdoc->findnodes('/x:me');
ok($node, 'Found because using document prefix');
$xdoc->registerNs( new => 'some_namespace' );
($node) = $xdoc->findnodes('/new:me');
ok($node, 'Found because using new specific prefix');
# Notice the change of namespace
my $uri = $xdoc->lookupNs('x');
is($uri, 'some_namespace', 'x prefix is some_namespace');
$xdoc->registerNs( x => 'other_namespace' );
$uri = $xdoc->lookupNs('x');
is($uri, 'some_namespace', 'x prefix is still some_namespace');
($node) = $xdoc->findnodes('/x:me');
ok(!$node, 'Not found because using document prefix was changed to diff namespace');
The basic problem is this: you get a XML document in which a namespace N is tied to a prefix P. I was expecting that if I set in my own parser prefix P to something else, that my local prefix P would take precedence over the document P. Apparently it does not.
Right now, what I did was this: before using XPath to match anything, I always used registerNs
method of XML::LibXML::XPathContext
to make sure I was getting the right elements. Now, I use this new function instead:
sub _safe_ns_register {
my ($xpc, $prefix, $ns) = @_;
while ($xpc->lookupNs($prefix)) {
$prefix++;
}
$xpc->registerNs($prefix => $ns);
return $prefix;
}
This function makes sure that the prefix I choose does not clash with any prefixes on the document. It returns the prefix I must use on my XPath expressions.
It solved my problem, but I still think that my local prefix should take precedence over the document one.