XPath如何处理XML命名空间?

Ada*_*dam 26 xml xpath xml-namespaces

XPath如何处理XML命名空间?

如果我使用

/IntuitResponse/QueryResponse/Bill/Id
Run Code Online (Sandbox Code Playgroud)

要解析下面的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>
Run Code Online (Sandbox Code Playgroud)

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

kjh*_*hes 40

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

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

建议您使用这些工具并定义名称空间前缀,然后可以根据需要使用这些名称前缀来限定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">
   ...
Run Code Online (Sandbox Code Playgroud)

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');
Run Code Online (Sandbox Code Playgroud)

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'})
Run Code Online (Sandbox Code Playgroud)

Python(ElementTree):

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

Java(SAX):

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

Java(XPath):

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

xmlstarlet:

-N i="http://schema.intuit.com/finance/v3"
Run Code Online (Sandbox Code Playgroud)

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 );
Run Code Online (Sandbox Code Playgroud)

PHP的:

使用DOMDocument改编自@ 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");
Run Code Online (Sandbox Code Playgroud)

又见@ PHP的SimpleXML的命名空间IMSOP的规范Q/A.

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);
Run Code Online (Sandbox Code Playgroud)

VBA:

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

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)
Run Code Online (Sandbox Code Playgroud)

Ruby(Nokogiri):

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

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

doc.remove_namespaces!
Run Code Online (Sandbox Code Playgroud)

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


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

/i:IntuitResponse/i:QueryResponse
Run Code Online (Sandbox Code Playgroud)

在XPath中击败名称空间(不推荐)

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

/*[local-name()='IntuitResponse']/*[local-name()='QueryResponse']/@startPosition
Run Code Online (Sandbox Code Playgroud)

或者,在XPath 2.0中:

/*:IntuitResponse/*:QueryResponse/@startPosition
Run Code Online (Sandbox Code Playgroud)

以这种方式绘制命名空间是有效的,但不推荐使用,因为它

  • Under-指定完整的元素/属性名称.
  • 无法区分不同命名空间中的元素/属性名称(命名空间的目的).请注意,可以通过添加额外的谓词来明确检查命名空间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
    
    Run Code Online (Sandbox Code Playgroud)

    1感谢Daniel Haleynamespace-uri()说明.

  • 过分冗长.

  • 可能值得补充的是,在 XPath 3.0/3.1 中,您可以编写“Q{http://example.com/ns}element”,而不是使用“foo:element”并在外部 API 中定义“foo”的绑定` 完全避免使用命名空间前缀。当额外的冗长无关紧要时(例如,如果 XPath 代码是软件生成的),这非常有用。 (3认同)
  • pugi:文档中的不合格声明 + 奇怪的行为观察 = 转身奔跑 / 生命太短暂。Javax:不要忘记调用 [**`setNamespaceAware(true)`**](http://docs.oracle.com/javase/6/docs/api/javax/xml/parsers/DocumentBuilderFactory.html#setNamespaceAware %28boolean%29) 在 `DocumentBuilderFactory` 上。 (2认同)
  • 事实证明,pugixml根本不支持xml名称空间(http://stackoverflow.com/questions/1042855/using-boost-to-read-and-write-xml-files).转身跑步. (2认同)
  • 答案的宝石。 (2认同)

归档时间:

查看次数:

7878 次

最近记录:

7 年,3 月 前