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

评论

您的XPath不应返回任何节点:INFO-XPath返回了0个项目(以0ms进行编译,以1ms进行评估)。您是如何执行XPath的?

@ har07我使用导入javax.xml.xpath.XPath在Java中完成了此操作。我同意使用在线测试仪无法正常工作。那是令人困惑的事情之一。

很好的问题! XPath本身没有提供指定默认名称空间或将名称空间前缀绑定到名称空间的方法。但是,幸运的是,托管语言和库也可以。有关详细信息,请参见下面的答案...

我对这个问题印象深刻,因为与大多数以前的提问者不同,Adam不仅包括了一个最小的可重现示例,而且他意识到并传达了XPath以某种方式处理XML名称空间的需求。大多数此类问题仅发布了XPath,可能还发布了一些XML(并且如果幸运的话,它不是图像或指向庞大的异地资源的链接),并声明“不起作用”。亚当(Adam)感觉到它与名称空间有关,确定了标题,并写下了我认为值得规范回答的问题。

可能与如何在XPath中使用Java中的名称空间查询XML重复吗?

#1 楼

在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">
   ...
 


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


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的查询


xmlstarlet:

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


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


请注意,如果默认名称空间定义了关联的名称空间前缀,则使用由nsResolver()返回的Document.createNSResolver()可以消除对客户nsResolver()的需求。

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


另请参阅@IMSoP的规范Q / A在PHP SimpleXML名称空间上。

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


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)
 


Ruby(Nokogiri):

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


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

 doc.remove_namespaces!
 


,但请注意以下警告,取消XML名称空间。


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

/i:IntuitResponse/i:QueryResponse



在XPath中贬低名称空间(不推荐)


另一种方法是编写可测试local-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()注释。

太冗长了。


评论


pugi:文档中的不合格声明+异常行为观察=转弯运行/寿命太短。 Javax:不要忘记在DocumentBuilderFactory上调用setNamespaceAware(true)。

–kjhughes
16-11-25在19:56



原来pugixml根本不支持xml名称空间(stackoverflow.com/questions/1042855/…)。转动并运行。

–亚当
16-11-25在21:10



@DougGlancy:抱歉,VBA是我尚未提供的少数示例之一。通过添加立即解决。请注意,不合格的属性名称不会自动放置在祖先元素上指定的默认名称空间中。我重新打开了您的其他问题,因为除了这个细节之外,您已经正确考虑了名称空间。在这里让我知道,如果我可以在XPath和名称空间方面提供一般帮助,或者可以针对您的问题提供帮助,或者可以针对您的问题提供帮助。谢谢。

–kjhughes
17年11月20日在4:09



谢谢,非常有帮助。

–道格·格兰西(Doug Glancy)
17年11月20日在4:18

宝石的答案。

– DebanjanB
8月24日10:14

#2 楼

我在Google工作表中使用/*[name()='...']从Wikidata获取一些计数。我有一个这样的表

 thes    WD prop links   items
 NOM     P7749   3925    3789
 AAT     P1014   21157   20224


cols linksitems中的公式分别是

=IMPORTXML("https://query.wikidata.org/sparql?query=SELECT(COUNT(*)as?c){?item wdt:"&$B14&"[]}","//*[name()='literal']")
=IMPORTXML("https://query.wikidata.org/sparql?query=SELECT(COUNT(distinct?item)as?c){?item wdt:"&$B14&"[]}","//*[name()='literal']")


。 SPARQL查询恰好没有任何空格...

我看到在Xml命名空间中使用name()而不是local-name()破坏了我的xpath !,并且由于某些原因//*:literal不起作用。