簡體   English   中英

為什么在使用命名空間時,XML :: LibXML沒有為此xpath查詢找到節點

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM