Pregunta

I am using XML::LibXML. In creating an XPath context, I need to be able to specify exactly which namespaces are available. However, all of the namespaces in the scope of the context node are automatically registered with the XPathContext object. I need to unregister those, but I get an error when I try to unregister a namespace which is in the scope of the context node:

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

my $xml = <<'__EOI__';
<?xml version="1.0"?>
<myDoc id="myDocId">
    <body id="bodyId">
    <baz:par xmlns:baz="www.baz.com"
             xmlns:bar="www.bar.com">
        <bar:id>xyz123</bar:id>
    </baz:par>
    </body>
</myDoc>
__EOI__

my $parser = XML::LibXML->new();
my $doc = $parser->parse_string($xml);

my $xpc = XML::LibXML::XPathContext->new();
$xpc->registerNs('baz', 'www.baz.com');

my $par = ${ $xpc->findnodes('//baz:par', $doc) }[0];

my $xpc2 = XML::LibXML::XPathContext->new($par);
$xpc2->unregisterNs('bar');

The above croaks XPathContext: cannot unregister namespace. Inspecting the source, I see that the error is printed from line 7618 of LibXML.xs. It is printed when the function xmlXPathRegisterNs returns -1. The only documentation for this function I can find is on xmlsoft.org. This documentation specifies that a -1 return value means that there was an error, but doesn't specify under what conditions an error occurs. I cannot for the life of me find the source for that method.

It may very well be that the XPath specification disallows this particular operation, but I am unable to determine that, either.

Can anyone tell me a) if there is a way to unregister namespaces in the scope of the context node using XML::LibXML::XPathContext or b) where there is documentation that this is not allowed in XPath?

EDIT

Joel showed me that unregistering a namespace only throws the given error if you haven't manually registered the namespace. However, unregistering still doesn't work right:

$xpc2->registerNs('bar', 'nothing'); #otherwise unregistering throws an error
$xpc2->unregisterNs('bar');
my @nodes = $xpc2->find('bar:id');
print scalar @nodes; #I want '0', but this prints '1'
¿Fue útil?

Solución

This is a limitation of the Perl bindings. XML::LibXML always registers all namespaces in scope of the context node. All you can do is to rebind an existing prefix to another namespace like you did in the edit section of your question. If you remove the call to unregisterNS, it should do what you want:

$xpc2->registerNs('bar', 'nothing'); # Rebind prefix
my @nodes = $xpc2->findnodes('bar:id', $par);
print scalar(@nodes), "\n"; # Prints 0

Otros consejos

Is it because the bar namespace has not been registered yet? If I add $xpc2->registerNs('bar', 'www.bar.com'); just before the unregisterNs call, the program runs fine for me. Does this produce the behavior you want?

Update from the additional information: The way you're doing your query may be revealing a bug in libxml or XML::LibXML; I'm not familiar enough with XPathContext to know if it is a bug or not. So I tried something that confuses me, I did a lookupNS after I did the unregisterNS and I still got the correct NS:

$xpc2->registerNs('bar', 'www.bar.com');
$xpc2->unregisterNs('bar');
print $xpc2->lookupNs('bar') . "\n"; # print www.bar.com

However, I may have a solution that does what you're looking for. Instead of using 'bar' as the prefix I tried using 'bob' instead and I think it may give you the behavior you're looking for:

$xpc2->registerNs('bob', 'www.bar.com');
$xpc2->unregisterNs('bob');
my @nodes = $xpc2->find('bob:id');
print scalar @nodes . "\n";

Doing it this way causes the find method to throw an exception. The code will not get to the print scalar command.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top