xml - XPath 如何处理 XML 命名空间?

标签 xml xpath xml-namespaces

XPath 如何处理 XML 命名空间?

如果我使用

/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 ?我想在这种情况下(因为只有一个 namespace )XPath 可以完全忽略 xmlns 。但如果有多个命名空间,事情可能会变得很糟糕。

最佳答案

XPath 1.0/2.0

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

XPath 本身没有办法将命名空间前缀与命名空间绑定(bind)。此类设施由托管图书馆提供。

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

<小时/>

以下是 XPath 主机提供的一些各种机制,用于指定命名空间前缀与命名空间 URI 的绑定(bind)。

(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);

Google 文档:

不幸的是,IMPORTXML()不提供命名空间前缀绑定(bind)机制。请参阅下一节击败 XPath 中的命名空间,了解如何使用 local-name() 作为解决方法。

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";
        // ...
       }
    });

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 );

请注意,如果默认命名空间定义了关联的命名空间前缀,则使用 Document.createNSResolver() 返回的 nsResolver()可以消除对客户 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()

博士:

改编自@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 支持删除 namespace ,

doc.remove_namespaces!

但请参阅以下警告,阻止破坏 XML namespace 。

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">
   ...
<小时/>

一旦声明了 namespace 前缀,就可以编写 XPath 来使用它:

/i:IntuitResponse/i:QueryResponse
<小时/>

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

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

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

或者,在 XPath 2.0 中:

/*:IntuitResponse/*:QueryResponse

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

  • Under-指定完整的元素/属性名称。

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

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

    感谢Daniel Haley对于 namespace-uri() 注释。

  • 过于冗长。

XPath 3.0/3.1

支持现代 XPath 3.0/3.1 的库和工具允许直接在 XPath 表达式中指定命名空间 URI:

/Q{http://schema.intuit.com/finance/v3}IntuitResponse/Q{http://schema.intuit.com/finance/v3}QueryResponse

虽然 Q{http://schema.intuit.com/finance/v3} 比使用 XML 命名空间前缀要详细得多,但它的优点是独立于托管库的命名空间前缀绑定(bind)机制。 Q{} 表示法以其创始人 James Clark 的名字命名为 Clark Notation。 W3C XPath 3.1 EBNF 语法将其称为 BracedURILiteral .

感谢Michael Kay获取涵盖 XPath 3.0/3.1 的 BracedURILiteral 的建议。

关于xml - XPath 如何处理 XML 命名空间?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59615988/

相关文章:

java - DBUnit 忽略 xml 元素

java - Xalan 和 document() 函数

java - Android - 使用 XPath 从 XML 检索值

python - 使用 lxml 保留子元素命名空间序列化

c# - 当您不知道命名空间时如何反序列化 XML?

c++ - QDomNode 命名空间检测工作不正确

java - Spring安全错误ApplicationEventMulticaster未初始化

java - 丢弃服务器 XML 响应中返回的不可打印字符

javascript - 将 XPath 添加到 chrome 扩展或 javascript