在上下文节点范围内取消注册名称空间

时间:2013-07-16 20:56:33

标签: xml perl xpath libxml2 xml-libxml

我正在使用XML :: LibXML。在创建XPath上下文时,我需要能够准确指定可用的命名空间。但是,上下文节点范围内的所有命名空间都会自动注册到XPathContext对象。我需要注销那些,但是当我尝试取消注册位于上下文节点范围内的命名空间时出现错误:

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');

上述呱呱叫XPathContext: cannot unregister namespace。检查源,我看到错误是从LibXML.xs的第7618行打印出来的。函数xmlXPathRegisterNs返回-1时打印。我能找到的这个函数的唯一文档是xmlsoft.org。此文档指定-1返回值表示存在错误,但未指定在何种条件下发生错误。我不能为我的生活找到那种方法的来源。

很可能XPath specification不允许这种特殊操作,但我也无法确定。

任何人都可以告诉我a)是否有办法使用XML :: LibXML :: XPathContext在上下文节点的范围内取消注册名称空间,或者b)哪里有文档表明XPath中不允许这样做?

修改

Joel告诉我,如果您没有手动注册命名空间,取消注册命名空间只会抛出给定的错误。但是,取消注册仍然无法正常工作:

$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'

2 个答案:

答案 0 :(得分:1)

这是Perl绑定的限制。 XML::LibXML总是registers all namespaces in scope of the context node。您所能做的就是将现有前缀重新绑定到另一个命名空间,就像在问题的编辑部分中所做的那样。如果您移除了对unregisterNS的呼叫,它应该按照您的意愿执行:

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

答案 1 :(得分:0)

是否因为bar命名空间尚未注册?如果我在$xpc2->registerNs('bar', 'www.bar.com');电话之前添加unregisterNs,程序运行正常。这会产生你想要的行为吗?

从其他信息更新: 您执行查询的方式可能是在libxml或XML :: LibXML中显示错误;我对XPathContext不太熟悉,不知道它是不是一个bug。所以我尝试了让我感到困惑的事情,在我完成lookupNS之后我做了unregisterNS并且我仍然得到了正确的NS:

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

但是,我可能有一个解决方案可以满足您的需求。我没有使用'bar'作为前缀,而是尝试使用'bob',而我认为它可能会为您提供所需的行为:

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

这样做会导致find方法抛出异常。代码无法访问print scalar命令。