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()
注释。 -
过于冗长。