XPath 如何处理 XML 名称空间? [英] How does XPath deal with XML namespaces?

查看:39
本文介绍了XPath 如何处理 XML 名称空间?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

XPath 如何处理 XML 名称空间?

如果我使用

/IntuitResponse/QueryResponse/Bill/Id

为了解析下面的 XML 文档,我得到了 0 个节点.

to parse the XML document below I get 0 nodes back.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<IntuitResponse xmlns="http://schema.intuit.com/finance/v3" 
                time="2016-10-14T10:48:39.109-07:00">
    <QueryResponse startPosition="1" maxResults="79" totalCount="79">
        <Bill domain="QBO" sparse="false">
            <Id>=1</Id>
        </Bill>
    </QueryResponse>
</IntuitResponse>

但是,我没有在 XPath 中指定命名空间(即 http://schema.intuit.com/finance/v3 不是路径的每个标记的前缀).如果我不明确告诉它,XPath 如何知道我想要哪个 Id?我想在这种情况下(因为只有一个命名空间)XPath 可以完全忽略 xmlns .但是如果有多个命名空间,事情就会变得很糟糕.

However, I'm not specifying the namespace in the XPath (i.e. http://schema.intuit.com/finance/v3 is not a prefix of each token of the path). How can XPath know which Id I want if I don't tell it explicitly? I suppose in this case (since there is only one namespace) XPath could get away with ignoring the xmlns entirely. But if there are multiple namespaces, things could get ugly.

推荐答案

在 XPath 中定义命名空间 (推荐)

XPath 本身没有办法将命名空间前缀与命名空间绑定.此类设施由托管库提供.

Defining namespaces in XPath (recommended)

XPath itself doesn't have a way to bind a namespace prefix with a namespace. Such facilities are provided by the hosting library.

建议您使用这些工具并定义命名空间前缀,然后根据需要使用这些前缀来限定 XML 元素和属性名称.

It is recommended that you use those facilities and define namespace prefixes that can then be used to qualify XML element and attribute names as necessary.

以下是 XPath 主机提供的用于指定名称空间前缀绑定到名称空间 URI 的各种机制中的一些.

Here are some of the various mechanisms which XPath hosts provide for specifying namespace prefix bindings to namespace URIs.

(OP 的原始 XPath,/IntuitResponse/QueryResponse/Bill/Id,已被省略为 /IntuitResponse/QueryResponse.)

C#:

XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("i", "http://schema.intuit.com/finance/v3");
XmlNodeList nodes = el.SelectNodes(@"/i:IntuitResponse/i:QueryResponse", nsmgr);

Java (SAX):

NamespaceSupport support = new NamespaceSupport();
support.pushContext();
support.declarePrefix("i", "http://schema.intuit.com/finance/v3");

Java (XPath):

xpath.setNamespaceContext(new NamespaceContext() {
    public String getNamespaceURI(String prefix) {
      switch (prefix) {
        case "i": return "http://schema.intuit.com/finance/v3";
        // ...
       }
    });

  • 记得打电话DocumentBuilderFactory.setNamespaceAware(true).
  • 另见:Java XPath:具有默认命名空间 xmlns 的查询
  • JavaScript:

    请参阅实现用户定义的命名空间解析器:

    function nsResolver(prefix) {
      var ns = {
        'i' : 'http://schema.intuit.com/finance/v3'
      };
      return ns[prefix] || null;
    }
    document.evaluate( '/i:IntuitResponse/i:QueryResponse', 
                       document, nsResolver, XPathResult.ANY_TYPE, 
                       null );
    

    Perl (LibXML):

    Perl (LibXML):

    my $xc = XML::LibXML::XPathContext->new($doc);
    $xc->registerNs('i', 'http://schema.intuit.com/finance/v3');
    my @nodes = $xc->findnodes('/i:IntuitResponse/i:QueryResponse');
    

    Python (lxml):

    Python (lxml):

    from lxml import etree
    f = StringIO('<IntuitResponse>...</IntuitResponse>')
    doc = etree.parse(f)
    r = doc.xpath('/i:IntuitResponse/i:QueryResponse', 
                  namespaces={'i':'http://schema.intuit.com/finance/v3'})
    

    Python (ElementTree):

    Python (ElementTree):

    namespaces = {'i': 'http://schema.intuit.com/finance/v3'}
    root.findall('/i:IntuitResponse/i:QueryResponse', namespaces)
    

    Python(Scrapy):

    Python (Scrapy):

    response.selector.register_namespace('i', 'http://schema.intuit.com/finance/v3')
    response.xpath('/i:IntuitResponse/i:QueryResponse').getall()
    

    哲学博士:

    改编自 @Tomalak's answer using DOMDocument:

    $result = new DOMDocument();
    $result->loadXML($xml);
    
    $xpath = new DOMXpath($result);
    $xpath->registerNamespace("i", "http://schema.intuit.com/finance/v3");
    
    $result = $xpath->query("/i:IntuitResponse/i:QueryResponse");
    

    另请参阅@IMSoP 关于 PHP SimpleXML 命名空间的规范问答.

    红宝石(Nokogiri):

    puts doc.xpath('/i:IntuitResponse/i:QueryResponse',
                    'i' => "http://schema.intuit.com/finance/v3")
    

    请注意,Nokogiri 支持删除命名空间,

    Note that Nokogiri supports removal of namespaces,

    doc.remove_namespaces!
    

    但请参阅下面的警告,劝阻 XML 命名空间的失败.

    but see the below warnings discouraging the defeating of XML namespaces.

    VBA:

    xmlNS = "xmlns:i='http://schema.intuit.com/finance/v3'"
    doc.setProperty "SelectionNamespaces", xmlNS  
    Set queryResponseElement =doc.SelectSingleNode("/i:IntuitResponse/i:QueryResponse")
    

    VB.NET:

    xmlDoc = New XmlDocument()
    xmlDoc.Load("file.xml")
    nsmgr = New XmlNamespaceManager(New XmlNameTable())
    nsmgr.AddNamespace("i", "http://schema.intuit.com/finance/v3");
    nodes = xmlDoc.DocumentElement.SelectNodes("/i:IntuitResponse/i:QueryResponse",
                                               nsmgr)
    

    SoapUI(doc):

    SoapUI (doc):

    declare namespace i='http://schema.intuit.com/finance/v3';
    /i:IntuitResponse/i:QueryResponse
    

    xmlstarlet:

    -N i="http://schema.intuit.com/finance/v3"
    

    XSLT:

    <xsl:stylesheet version="1.0"
                    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                    xmlns:i="http://schema.intuit.com/finance/v3">
       ...
    

    请注意,如果默认命名空间定义了关联的命名空间前缀,则使用 Document.createNSResolver() 可以消除对客户 nsResolver() 的需求.

    Note that if the default namespace has an associated namespace prefix defined, using the nsResolver() returned by Document.createNSResolver() can obviate the need for a customer nsResolver().

    一旦声明了命名空间前缀,就可以编写 XPath 来使用它:

    Once you've declared a namespace prefix, your XPath can be written to use it:

    /i:IntuitResponse/i:QueryResponse
    


    击败XPath中的命名空间(不推荐)

    另一种方法是编写对 local-name() 进行测试的谓词:

    /*[local-name()='IntuitResponse']/*[local-name()='QueryResponse']
    

    或者,在 XPath 2.0 中:

    Or, in XPath 2.0:

    /*:IntuitResponse/*:QueryResponse
    

    以这种方式绕过命名空间是可行的,但不推荐,因为它

    Skirting namespaces in this manner works but is not recommended because it

    • 未指定完整的元素/属性名称.

    • Under-specifies the full element/attribute name.

    无法区分不同的元素/属性名称命名空间(命名空间的真正目的).请注意,可以通过添加额外的谓词来显式检查命名空间 URI 来解决这个问题1:

    Fails to differentiate between element/attribute names in different namespaces (the very purpose of namespaces). Note that this concern could be addressed by adding an additional predicate to check the namespace URI explicitly1:

     /*[    namespace-uri()='http://schema.intuit.com/finance/v3' 
        and local-name()='IntuitResponse']
     /*[    namespace-uri()='http://schema.intuit.com/finance/v3' 
        and local-name()='QueryResponse']
    

    1感谢 Daniel Haleynamespace-uri() 注意.

    过于冗长.

    这篇关于XPath 如何处理 XML 名称空间?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆