[英]Why does XML::LibXML find no nodes for this xpath query when using a namespace
我正在嘗試使用XPath查詢選擇一個節點,我不明白為什么XML :: LibXML在有xmlns屬性時找不到該節點。 這是一個演示此問題的腳本:
#!/usr/bin/perl
use XML::LibXML; # 1.70 on libxml2 from libxml2-dev 2.6.16-7sarge1 (don't ask)
use XML::XPath; # 1.13
use strict;
use warnings;
use v5.8.4; # don't ask
my ($xpath, $libxml, $use_namespace) = @ARGV;
my $xml = sprintf(<<'END_XML', ($use_namespace ? 'xmlns="http://www.w3.org/2000/xmlns/"' : q{}));
<?xml version="1.0" encoding="iso-8859-1"?>
<RootElement>
<MyContainer %s>
<MyField>
<Name>ID</Name>
<Value>12345</Value>
</MyField>
<MyField>
<Name>Name</Name>
<Value>Ben</Value>
</MyField>
</MyContainer>
</RootElement>
END_XML
my $xml_parser
= $libxml ? XML::LibXML->load_xml(string => $xml, keep_blanks => 1)
: XML::XPath->new(xml => $xml);
my $nodecount = 0;
foreach my $node ($xml_parser->findnodes($xpath)) {
$nodecount ++;
print "--NODE $nodecount--\n"; #would use say on newer perl
print $node->toString($libxml && 1), "\n";
}
unless ($nodecount) {
print "NO NODES FOUND\n";
}
此腳本允許您在XML :: LibXML解析器和XML :: XPath解析器之間進行選擇。 它還允許您在MyContainer元素上定義xmlns屬性,或者根據傳遞的參數將其保留。
我正在使用的xpath表達式是“RootElement / MyContainer”。 當我使用沒有命名空間的XML :: LibXML解析器運行查詢時,它找到沒有問題的節點:
benb@enkidu:~$ ROC/ECG/libxml_xpath.pl 'RootElement/MyContainer' libxml
--NODE 1--
<MyContainer>
<MyField>
<Name>ID</Name>
<Value>12345</Value>
</MyField>
<MyField>
<Name>Name</Name>
<Value>Ben</Value>
</MyField>
</MyContainer>
但是,當我使用命名空間運行它時,它找不到節點:
benb@enkidu:~$ ROC/ECG/libxml_xpath.pl 'RootElement/MyContainer' libxml use_namespace
NO NODES FOUND
將此與使用XMLL :: XPath解析器時的輸出進行對比:
benb@enkidu:~$ ROC/ECG/libxml_xpath.pl 'RootElement/MyContainer' 0 # no namespace
--NODE 1--
<MyContainer>
<MyField>
<Name>ID</Name>
<Value>12345</Value>
</MyField>
<MyField>
<Name>Name</Name>
<Value>Ben</Value>
</MyField>
</MyContainer>
benb@enkidu:~$ ROC/ECG/libxml_xpath.pl 'RootElement/MyContainer' 0 1 # with namespace
--NODE 1--
<MyContainer xmlns="http://www.w3.org/2000/xmlns/">
<MyField>
<Name>ID</Name>
<Value>12345</Value>
</MyField>
<MyField>
<Name>Name</Name>
<Value>Ben</Value>
</MyField>
</MyContainer>
哪些解析器實現正確“正確”? 當我使用命名空間時,為什么XML :: LibXML會以不同的方式對待它? 在命名空間到位時,我該怎么做才能檢索節點?
這是一個FAQ。 XPath認為表達式中任何未加前綴的名稱都屬於“無名稱空間”。
然后,表達式:
RootElement/MyContainer
選擇屬於“無命名空間”的所有MyContainer
元素,並且它們是屬於“無命名空間”的所有RootElement
元素的子元素,並且是上下文的子元素(當前節點)。 但是,整個文檔中根本沒有屬於“無命名空間”的元素 - 所有元素都屬於默認命名空間。
這解釋了您獲得的結果。 XML :: LibXML 是對的。
常見的解決方案是托管語言的API允許通過“注冊”命名空間將特定前綴綁定到命名空間。 然后可以使用如下表達式:
x:RootElement/x:MyContainer
其中x
是注冊名稱空間的前綴。
在托管語言不提供注冊命名空間的極少數情況下 ,請使用以下表達式:
*[name()='RootElement']/*[name()='MyContainer']
@Dmitre是對的。 您需要查看XML :: LibXML :: XPathContext ,它將允許您聲明命名空間,然后您可以使用名稱空間感知XPath語句。 我給出了一個在stackoverflow上使用它的例子 - 看看我為什么要在Perl的XML :: LibXML中使用XPathContext
使用XML :: LibXML 1.69。
也許這是一個XML :: LibXML 1.69的東西,但奇怪的是我可以使用普通的XPath和findnodes(),下面的代碼打印節點。
use strict;
use XML::LibXML;
my $xml = <<END_XML;
<?xml version="1.0" encoding="iso-8859-1"?>
<RootElement>
<MyContainer xmlns="http://www.w3.org/2000/xmlns/">
<MyField>
<Name>ID</Name>
<Value>12345</Value>
</MyField>
<MyField>
<Name>Name</Name>
<Value>Ben</Value>
</MyField>
</MyContainer>
</RootElement>
END_XML
my $parser = XML::LibXML->new();
$parser->recover_silently(1);
my $doc = $parser->parse_string($xml);
my $root = $doc->documentElement();
foreach my $node ($root->findnodes('MyContainer/MyField')) {
print $node->toString();
}
但是,如果我將命名空間更改為“http://www.w3.org/2000/xmlns/”之外的其他名稱,則需要使用XML :: LibXML :: XPathContext來獲取要打印的相同節點。
use strict;
use XML::LibXML;
my $xml = <<END_XML;
<?xml version="1.0" encoding="iso-8859-1"?>
<RootElement>
<MyContainer xmlns="http://something.org/2000/something/">
<MyField>
<Name>ID</Name>
<Value>12345</Value>
</MyField>
<MyField>
<Name>Name</Name>
<Value>Ben</Value>
</MyField>
</MyContainer>
</RootElement>
END_XML
my $parser = XML::LibXML->new();
$parser->recover_silently(1);
my $doc = $parser->parse_string($xml);
my $root = $doc->documentElement();
my $xpc = XML::LibXML::XPathContext->new($root);
$xpc->registerNs("x", "http://something.org/2000/something/");
foreach my $node ($xpc->findnodes('x:MyContainer/x:MyField')) {
print $node->toString();
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.