xml - XPath 如何处理 XML namespace ?

标签 xml xpath xml-namespaces

XPath 如何处理 XML namespace ?

如果我使用

/IntuitResponse/QueryResponse/Bill/Id

为了解析下面的 XML 文档,我得到了 0 个节点。
<?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完全。但是如果有多个命名空间,事情可能会变得很糟糕。

最佳答案

在 XPath 中定义命名空间(推荐)
XPath 本身没有办法将命名空间前缀与命名空间绑定(bind)。这些设施由托管图书馆提供。
建议您使用这些工具并定义命名空间前缀,然后根据需要使用这些前缀来限定 XML 元素和属性名称。

以下是 XPath 主机提供的一些用于指定 namespace 前缀绑定(bind)到 namespace URI 的各种机制。
(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: Queries with default namespace xmlns

  • JavaScript:
    Implementing a User Defined Namespace Resolver :
    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 );
    
    请注意,如果默认命名空间定义了关联的命名空间前缀,则使用 nsResolver() Document.createNSResolver() 返回可以消除对客户的需求nsResolver() .
    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):
    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):
    namespaces = {'i': 'http://schema.intuit.com/finance/v3'}
    root.findall('/i:IntuitResponse/i:QueryResponse', namespaces)
    
    python (Scrapy):
    response.selector.register_namespace('i', 'http://schema.intuit.com/finance/v3')
    response.xpath('/i:IntuitResponse/i:QueryResponse').getall()
    
    php:
    改编自 @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's canonical Q/A on PHP SimpleXML namespaces .
    ruby (Nokogiri):
    puts doc.xpath('/i:IntuitResponse/i:QueryResponse',
                    'i' => "http://schema.intuit.com/finance/v3")
    
    请注意,Nokogiri 支持删除命名空间,
    doc.remove_namespaces!
    
    但请参阅以下警告,这些警告阻止了 XML 命名空间的失败。
    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):
    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">
       ...
    

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

    在 XPath 中击败命名空间(不推荐)
    另一种方法是编写针对 local-name() 进行测试的谓词。 :
    /*[local-name()='IntuitResponse']/*[local-name()='QueryResponse']
    
    或者,在 XPath 2.0 中:
    /*:IntuitResponse/*:QueryResponse
    
    以这种方式绕过命名空间是可行的,但不推荐,因为它
  • 未指定完整的元素/属性名称。
  • 无法区分不同的元素/属性名称
    命名空间(命名空间的真正目的)。请注意,可以通过添加一个额外的谓词来明确检查命名空间 URI 来解决这个问题1:
     /*[    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 Haley对于namespace-uri()笔记。
  • 过于冗长。
  • 关于xml - XPath 如何处理 XML namespace ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48050983/

    相关文章:

    xml - 在 xml 中存储 url 的正确方法?

    json - "[SyntaxError: Unexpected token <]"当我尝试使用我的 Node/Express 应用程序将文档添加到我的 Solr 索引时

    sql-server - 如何将SQL查询结果保存到磁盘上的XML文件

    xml - VBScript从XML节点分配变量

    html - XPath 来定位包含文本节点的元素?

    xml - 通过命令行替换 xml 上的属性

    Java Xml 到对象?

    xml - 动态改变按钮的 Action

    javascript - DOMParser 或 XMLSerializer 正在删除我在 IE9 中的命名空间声明

    xml - 命名空间错误 : Namespace prefix bd on keyword is not defined