XPath (故意)不是为您希望对仅存在于 XML 文档中的某些未知名称空间使用相同 XPath 表达式的情况而设计的。您应该提前知道命名空间,向 XPath 处理器声明命名空间,并在表达式中使用该名称。Martin 和 Dan 的回答展示了如何在 C# 中执行此操作。
这种困难的原因在XML 命名空间规范中得到了最好的表达:
我们设想可扩展标记语言 (XML) 的应用,其中单个 XML 文档可能包含为多个软件模块定义和使用的元素和属性(这里称为“标记词汇表”)。这样做的一个动机是模块化:如果存在这样一个易于理解的标记词汇表并且有可用的有用软件,那么最好重用这个标记而不是重新发明它。
此类包含多个标记词汇的文档会带来识别和冲突的问题。软件模块需要能够识别它们被设计处理的元素和属性,即使面对在用于某些其他软件包的标记使用相同的元素名称或属性名称时发生的“冲突”。
这些考虑要求文档构造应该具有构造的名称,以避免来自不同标记词汇的名称之间的冲突。本规范描述了一种机制,即 XML 名称空间,它通过为元素和属性分配扩展名称来实现这一点。
也就是说,命名空间应该用来确保你知道你的文档在说什么:那个<head>
元素是在谈论 XHTML 文档的序言还是 AnatomyML 文档中的某些人?您永远不会“假定”对名称空间不可知,这几乎是您应该在任何 XML 词汇表中定义的第一件事。
应该可以做你想做的事,但我不认为它可以在单个 XPath 表达式中完成。首先,您需要在文档中翻找并提取所有命名空间URI,然后将它们添加到命名空间管理器中,然后运行您想要的实际 XPath 表达式(并且您需要了解有关文档中命名空间分布的信息)点,或者你有很多表达式要运行)。我认为您可能最好使用 XPath 以外的东西(例如 DOM 或类似 SAX 的 API)来查找命名空间URI,但您也可以探索 XPath 命名空间轴(在 XPath 1.0 中),使用namespace-uri-from-QName
函数(在 XPath 2.0 中)或使用像 Oleg's 这样的表达方式"configuration/*[local-name() = 'MyNode']"
. 无论如何,我认为最好的办法是尽量避免编写与命名空间无关的 XPath!为什么你不提前知道你的命名空间?你将如何避免匹配你不打算匹配的东西?
编辑 - 你知道 namespaceURI 吗?
所以事实证明,你的问题让我们所有人都感到困惑。显然您知道名称空间 URI,但您不知道 XML 文档中使用的名称空间前缀。实际上,在这种情况下,没有使用命名空间前缀,并且 URI 成为定义它的默认命名空间。要知道的关键是所选前缀(或缺少前缀)与您的 XPath 表达式(以及一般的 XML 解析)无关。当文档表示为文本时,前缀/xmlns 属性只是将节点与命名空间 URI 相关联的一种方式。您可能想看看这个答案,我在其中尝试澄清名称空间前缀。
您应该尝试以与解析器相同的方式来考虑 XML 文档——每个节点都有一个名称空间 URI 和一个本地名称。命名空间前缀/继承规则只是节省了多次输入 URI。写下来的一种方法是使用 Clark 表示法:也就是说,您编写 { http://www.example.com/namespace/example }LocalNodeName,但这种表示法通常只用于文档 - XPath 对这种表示法一无所知。
相反,XPath 使用自己的命名空间前缀。类似/ns1:root/ns2:node
. 但这些与可能在原始 XML 文档中使用的任何前缀完全分开,并且没有任何关系。任何 XPath 实现都可以通过命名空间 URI 映射它自己的前缀。对于 C# 实现,您使用XmlNamespaceManager
,在 Perl 中您提供哈希, xmllint 接受命令行参数...所以您需要做的就是为您知道的命名空间 URI 创建一些任意前缀,并在 XPath 表达式中使用此前缀。使用什么前缀并不重要,在 XML 中,您只关心 URI 和 localName 的组合。
另一件要记住的事情(这常常令人惊讶)是 XPath 不进行命名空间继承。无论命名空间是来自继承、xmlns 属性还是命名空间前缀,您都需要为每个具有命名空间的对象添加前缀。此外,尽管您应该始终考虑 URI 和 localNames,但也有一些方法可以从 XML 文档中访问前缀。很少需要使用这些。