为啥命名空间限定节点没有 XPath 语法?
Posted
技术标签:
【中文标题】为啥命名空间限定节点没有 XPath 语法?【英文标题】:Why is there no XPath syntax for namespace-qualified nodes?为什么命名空间限定节点没有 XPath 语法? 【发布时间】:2011-10-26 18:26:54 【问题描述】:XML 文档中的某些节点具有名称空间,并使用定义的前缀指定。
可以在 XPath 1.0 中指定 local-name() 从而忽略命名空间。
但是,我想让 XPath 的编写者能够使用它们的完整命名空间限定名称作为标识符来查找节点。
推荐的方法是在调用代码(在我的例子中是 Java)中添加命名空间声明。但这意味着编写 Xpath 的人没有使用命名空间的能力!
我们如何使用纯 XPath 通过它们的完全限定名称来查找节点?
【问题讨论】:
如果我正确理解了这个问题,您是在问为什么需要使用像addNamespace("abc","http://example.com")
这样的指令来声明命名空间,然后允许执行像 /abc:node
这样的 Xpath 查询,而不是不知何故直接在查询中使用http://example.com
。我是否正确解释了这个问题?
@Jong Bor 是的,就是这样。在 XPath 中以某种方式声明 abc=example.com 之后,直接在 XPath 查询中使用前缀 abc 会很好。我知道 XPath 表达式很短,通常不会在其中插入定义,但从技术上来说,没有什么可以阻止在 XPath 中实现这一点。
BTW 好问题,+1。由于前缀应该只是无关紧要的语法糖,而命名空间 URI 才是重要的,所以您会认为匹配节点名称是命名空间 URI 可能很有用,而不必弄乱前缀——尤其是如果 XPath 本身提供没有办法声明前缀。
【参考方案1】:
不确定您所说的“作为标识符”是什么意思。
我们如何使用纯 XPath 通过它们的完全限定名称来查找节点?
在 XPath 1.0 中,通过使用 local-name() 和 namespace-uri(),例如
"*[local-name() = 'foo' and namespace-uri() = 'http://my.org/ns/2.0']"
在 XPath 2.0 中,有一组更丰富的与命名空间相关的函数,例如namespace-uri-from-QName()
。但我不确定他们是否会根据您的需要改进上述内容。
【讨论】:
好,有道理。有点笨拙——我宁愿写像my.org/ns/2.0|foo这样的东西,没有这些函数名,甚至更好地使用前缀,在XPath而不是Java中声明它们——但这确实符合要求。 通过“使用它们的完整命名空间限定名称作为标识符”我只是意味着您应该能够使用命名空间+本地名称精确引用节点。你的回答说明了如何。 @JoshuaFox:我同意,能够在 XPath 本身中声明前缀会非常方便。我想“他们”决定不将它包含在 XPath 中是有一些原因的,但这似乎确实是一个很大的差距,因为解决方法太冗长了。 @LarsH 这种表达方式适用于属性选择吗? @ArtemOboturov:是的。只需将*
更改为@*
。 (请注意,属性不会继承其祖先的默认命名空间。)【参考方案2】:
您可以在 XPath 查询期间使用命名空间。在 Java 中,如果您还想在这些查询中始终使用前缀而不是完全限定的命名空间,则需要提供 NamespaceContext 的实现。只需将 NamespaceContext
的实例添加到您的 XPath
- 我假设您使用标准的 JDK 实现 - 但这个概念也适用于 Jaxen 或其他人。
然后就可以执行//customns:Element
等查询了。
如果您不使用或不能使用NamespaceContext
(无论出于何种原因),那么唯一的解决方案似乎是使用local-name
和namespace-uri
函数:
Document doc = ...;
XPath xp = XPathFactory.newInstance().newXPath();
String name = "Element";
String ns = "http://www.custom.org/#";
String expr = "//*[local-name() = '"+name+" and namespace-uri() = '"+ns+"']";
Node node = ((NodeList)xp.evaluate(expr, doc, XPathConstants.NODESET)).item(0);
【讨论】:
谢谢。如果我愿意使用完整的命名空间名称而不是前缀怎么办?换句话说,类似的东西。我可以避免在 Java 中声明命名空间吗?我的目标是从外部源读取 XPath 表达式,因此我宁愿不必单独指定。 这取决于源的样子——如果你只有一个表达式 //abc:Element 而没有命名空间声明,那么理论上你会被卡住——无法知道 abc 指向什么。但是,如果您可以绝对确定这些外部表达式与您的工作文档具有相同的前缀,那么您仍然可以在文档中查找相应的命名空间,然后构建一个 NamespaceContext 或根本不使用它。我将更新我的答案,概述如何这样做。 不幸的是,似乎有no other solution 而不是 local-name() 和 namespace-uri() 的组合。【参考方案3】:目前处于工作草案状态的 XPath 3.0 将包含 URI qualified QNames 的文字表达式,允许直接指定命名空间 uri。
以下是 EQName 的一些示例:
pi 是一个没有命名空间前缀的词法 QName。 math:pi 是一个带有命名空间前缀的词法 QName。 "http://www.w3.org/2005/xpath-functions/math":pi 使用URILiteral 指定命名空间URI;它不是一个词法 QName。
我认为Saxon 9.3 包含 xpath 3.0 的预览实现,它应该可以通过 java api 使用。
【讨论】:
正确,但目前只能在产品的商业版本中启用 3.0 功能。诸如“为什么早期版本中没有此功能”之类的问题总是很难回答 - 要么没有人提出它,要么他们没有足够努力,或者他们不得不与竞争对手的提案竞争以解决相同的问题。【参考方案4】:XPath 3.0 的规范说:
Qhttp://www.w3.org/2005/xpath-functions/mathpi
目前(2015 年 10 月)适用,例如在 eXist-db 中。
【讨论】:
@e4c5:XML 喜欢在其文字语法中使用 URI。您应该更仔细地阅读答案。 @NathanTuggy 是的,您是对的,在审核时确实显示为仅链接的答案(评论由系统自动插入)我现在已删除该评论。以上是关于为啥命名空间限定节点没有 XPath 语法?的主要内容,如果未能解决你的问题,请参考以下文章