XPath如何处理XML名称空间?

XPath如何处理XML名称空间?

如果我使用

/IntuitResponse/QueryResponse/Bill/Id 

parsing下面的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不是path中每个标记的前缀)。 如果我没有明确地告诉XPath XPath如何知道我想要的Id ? 我想在这种情况下(因为只有一个名称空间),XPath可以完全忽略xmlns 。 但是,如果有多个命名空间,事情可能会变得丑陋。

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

XPath本身没有办法将名称空间前缀与名称空间绑定。 这些设施由托pipe图书馆提供。

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


以下是XPath主机为命名空间URI指定名称空间前缀绑定所提供的各种机制:

XSLT:

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

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':'/i:IntuitResponse/i:QueryResponse'}) 

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进行查询

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

xmlstarlet:

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

JavaScript的:

请参阅实现用户定义的名称空间parsing器 :

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

PHP的:

改编自@ Tomalak的回答 :

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

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

 /i:IntuitResponse/i:QueryResponse 

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

另一种方法是编写用于testinglocal-name()谓词:

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

或者,在XPath 2.0中:

 /*:IntuitResponse/*:QueryResponse/@startPosition 

以这种方式踢脚本命名空间的作品,但不推荐,因为它

  • 低于指定完整的元素/属性名称。
  • 无法区分不同名称空间中的元素/属性名称(命名空间的目的)。 请注意,可以通过添加一个额外的谓词来显式检查名称空间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'] /@startPosition 

    1感谢Daniel Haley提供的namespace-uri()注释。

  • 过于冗长。