提问人:Adam 提问时间:11/25/2016 最后编辑:kjhughesAdam 更新时间:12/14/2022 访问量:28127
XPath 如何处理 XML 命名空间?
How does XPath deal with 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 中指定命名空间(即 不是路径的每个标记的前缀)。如果我不明确告诉 XPath 我怎么知道我想要哪个?我想在这种情况下(因为只有一个命名空间),XPath 可以完全忽略它。但是,如果有多个命名空间,事情可能会变得丑陋。http://schema.intuit.com/finance/v3
Id
xmlns
答:
XPath 1.0/2.0 版本
在 XPath 中定义命名空间(推荐)
XPath 本身没有办法将命名空间前缀与命名空间绑定。这些设施由托管图书馆提供。
建议您使用这些工具并定义命名空间前缀,然后可以使用这些前缀根据需要限定 XML 元素和属性名称。
以下是 XPath 主机提供的一些各种机制,用于指定命名空间前缀绑定到命名空间 URI。
(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);
谷歌文档:
不幸的是,IMPORTXML()
没有提供命名空间前缀绑定机制。请参阅下一节 Defeating namespaces in 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的:
请参阅实现用户定义的命名空间解析程序:
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()
返回的 可以消除对 customer 的需求。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'})
蟒蛇 (ElementTree):
namespaces = {'i': 'http://schema.intuit.com/finance/v3'}
root.findall('/i:IntuitResponse/i:QueryResponse', namespaces)
蟒蛇(Scrapy):
response.selector.register_namespace('i', 'http://schema.intuit.com/finance/v3')
response.xpath('/i:IntuitResponse/i:QueryResponse').getall()
磷酸:
改编自 @Tomalak 使用 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 关于 PHP SimpleXML 命名空间的规范 Q/A。
红宝石 (Nokogiri):
puts doc.xpath('/i:IntuitResponse/i:QueryResponse',
'i' => "http://schema.intuit.com/finance/v3")
请注意,Nokogiri 支持删除命名空间,
doc.remove_namespaces!
但请参阅以下警告,这些警告不鼓励失败 XML 命名空间。
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 (文档):
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">
...
声明命名空间前缀后,可以编写 XPath 以使用它:
/i:IntuitResponse/i:QueryResponse
在 XPath 中击败命名空间(不推荐)
另一种方法是编写针对以下条件进行测试的谓词:local-name()
/*[local-name()='IntuitResponse']/*[local-name()='QueryResponse']
或者,在 XPath 2.0 中:
/*:IntuitResponse/*:QueryResponse
以这种方式绕过命名空间可以工作,但不建议这样做,因为它
指定完整的元素/属性名称。
无法区分不同 命名空间(命名空间的真正目的)。请注意,可以通过添加一个额外的谓词来显式检查命名空间 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
虽然比使用 XML 命名空间前缀要详细得多,但它的优点是独立于宿主库的命名空间前缀绑定机制。该符号以其创始人詹姆斯·克拉克(James Clark)的名字被称为克拉克符号。W3C XPath 3.1 EBNF 语法称其为 BracedURILiteral
。Q{http://schema.intuit.com/finance/v3}
Q{}
感谢 Michael Kay 对 XPath 3.0/3.1 的 BracedURILiteral
的建议。
评论
foo:element
foo
Q{http://example.com/ns}element
我在谷歌表格中用于从维基数据中获取一些计数。我有一张这样的桌子/*[name()='...']
thes WD prop links items
NOM P7749 3925 3789
AAT P1014 21157 20224
和 cols 中的公式是links
items
=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 查询恰好没有任何空格...
我看到 used 而不是在 Xml 命名空间中破坏了我的 xpath!,并且由于某种原因不起作用。name()
local-name()
//*:literal
评论
javax.xml.xpath.XPath