代码之家  ›  专栏  ›  技术社区  ›  benrifkah

为什么XML::LibXML在使用命名空间时找不到此xpath查询的节点

  •  6
  • benrifkah  · 技术社区  · 15 年前

    我正试图使用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对它的处理不同?当命名空间就位时,如何检索节点?

    3 回复  |  直到 15 年前
        1
  •  14
  •   Dimitre Novatchev    15 年前

    这是一个常见问题。XPath认为表达式中任何未固定的名称都属于“无命名空间”。

    RootElement/MyContainer
    

    全选 MyContainer 属于“无命名空间”且是所有 RootElement 属于“无命名空间”且是上下文(当前节点)的子级的元素。但是,整个文档中根本没有属于“无命名空间”的元素——所有元素都属于默认命名空间。

    正确的。

    宿主语言的API允许通过“注册”命名空间将特定前缀绑定到命名空间。然后可以使用这样的表达式:

    x:RootElement/x:MyContainer
    

    x 是命名空间已注册的前缀。

    ,使用以下表达式:

    *[name()='RootElement']/*[name()='MyContainer']
    
        2
  •  7
  •   Community Mohan Dere    8 年前

    @德米特里是对的。你得看看 XML::LibXML::XPathContext 这将允许您声明命名空间,然后可以使用支持命名空间的XPath语句。前段时间我在stackoverflow上举了一个例子-看看 Why should I use XPathContext with Perl's XML::LibXML

        3
  •  1
  •   bob.faist    15 年前

    也许这是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();
    }
    

    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();
    }
    
    推荐文章