如何在没有前缀的默认命名空间中使用 XPath?
用于从此文档查询所有 MyNode 的 XPath(在 C# API 中为 XDocument.XPathSelectElements(xpath, nsman),如果重要的话)是什么?
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<MyNode xmlns="lcmp" attr="true">
<subnode />
</MyNode>
</configuration>
- 我尝试了
/configuration/MyNode
这是错误的,因为它忽略了名称空间。 - 我尝试了
/configuration/lcmp:MyNode
这是错误的,因为lcmp
是 URI,而不是前缀。 - 我尝试了
/configuration/{lcmp}MyNode
但失败了,因为其他信息:'/configuration/{lcmp}MyNode' 有一个无效的令牌。
编辑:我无法使用mgr.AddNamespace("df", "lcmp");
正如一些回答者所建议的那样。这要求 XML 解析程序提前知道我计划使用的所有名称空间。由于这适用于任何源文件,因此我不知道要手动添加哪些命名空间的前缀。看起来 {my uri}
是 XPath 语法,但 Microsoft 并没有费心实现它......真的吗?
What is the XPath (in C# API to XDocument.XPathSelectElements(xpath, nsman) if it matters) to query all MyNodes from this document?
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<MyNode xmlns="lcmp" attr="true">
<subnode />
</MyNode>
</configuration>
- I tried
/configuration/MyNode
which is wrong because it ignores the namespace. - I tried
/configuration/lcmp:MyNode
which is wrong becauselcmp
is the URI, not the prefix. - I tried
/configuration/{lcmp}MyNode
which failed becauseAdditional information: '/configuration/{lcmp}MyNode' has an invalid token.
EDIT: I can't use mgr.AddNamespace("df", "lcmp");
as some of the answerers have suggested. That requires that the XML parsing program know all the namespaces I plan to use ahead of time. Since this is meant to be applicable to any source file, I don't know which namespaces to manually add prefixes for. It seems like {my uri}
is the XPath syntax, but Microsoft didn't bother implementing that... true?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
configuration
元素位于未命名的命名空间中,MyNode 绑定到lcmp
命名空间,没有命名空间前缀。此 XPATH 语句将允许您在不声明
lcmp
命名空间或在 XPATH 中使用命名空间前缀的情况下寻址MyNode
元素:它与任何元素是
configuration
的子元素,然后使用带有namespace-uri()
和local-name()
函数将其限制为MyNode
元素。如果您不知道元素将使用哪个命名空间 uri,那么您可以使 XPATH 更通用,并仅匹配
local-name()
:但是,您可能会面临匹配不同词汇表(绑定到不同名称空间 uri)中碰巧使用相同名称的不同元素的风险。
The
configuration
element is in the unnamed namespace, and the MyNode is bound to thelcmp
namespace without a namespace prefix.This XPATH statement will allow you to address the
MyNode
element without having declared thelcmp
namespace or use a namespace prefix in your XPATH:It matches any element that is a child of
configuration
and then uses a predicate filer withnamespace-uri()
andlocal-name()
functions to restrict it to theMyNode
element.If you don't know which namespace-uri's will be used for the elements, then you can make the XPATH more generic and just match on the
local-name()
:However, you run the risk of matching different elements in different vocabularies(bound to different namespace-uri's) that happen to use the same name.
您需要使用 XmlNamespaceManager,如下所示:
You need to use an XmlNamespaceManager as follows:
XPath(故意)不适合这样的情况:您希望对仅存在于 XML 文档中的某些未知名称空间使用相同的 XPath 表达式。您应该提前知道名称空间,向 XPath 处理器声明名称空间,并在表达式中使用该名称。 Martin 和 Dan 的答案展示了如何在 C# 中执行此操作。
这种困难的原因在 XML 命名空间 规范中得到了最好的表达:
也就是说,命名空间应该用于确保您知道文档正在谈论什么:
元素是在谈论 XHTML 文档的前导码还是在 AnatomyML 文档中谈论某人的头?您永远“不应该”对命名空间一无所知,而且它几乎是您应该在任何 XML 词汇表中定义的第一件事。
应该可以做你想做的事,但我不认为它可以在单个 XPath 表达式中完成。首先,您需要在文档中翻查并提取所有名称空间URI,然后将它们添加到名称空间管理器,然后运行您想要的实际 XPath 表达式(并且您需要了解有关文档中名称空间分布的一些信息)点,或者你有很多表达式要运行)。我认为您可能最好使用 XPath 之外的其他东西(例如 DOM 或类似 SAX 的 API)来查找名称空间URI,但您也可以探索 XPath 名称空间轴(在 XPath 1.0 中),使用
namespace-uri-from-QName
函数(在 XPath 2.0 中)或使用像 Oleg 的"configuration/*[local-name() = 'MyNode']"
这样的表达式。不管怎样,我认为最好的选择是尝试避免编写与命名空间无关的 XPath!为什么你不提前知道你的命名空间?您将如何避免匹配您不打算匹配的东西?编辑 - 你知道namespaceURI吗?
所以事实证明你的问题让我们所有人都感到困惑。显然您知道命名空间 URI,但不知道 XML 文档中使用的命名空间前缀。事实上,在这种情况下,没有使用命名空间前缀,并且 URI 成为定义它的默认命名空间。要知道的关键是所选的前缀(或缺少前缀)与您的 XPath 表达式(以及一般的 XML 解析)无关。当文档表示为文本时, prefix / xmlns 属性只是将节点与名称空间 URI 关联的一种方法。您可能想看看 这个答案,我尝试澄清命名空间前缀。
您应该尝试以解析器思考 XML 文档的方式思考它 - 每个节点都有一个名称空间 URI 和一个本地名称。命名空间前缀/继承规则只是节省了多次键入 URI 的时间。记下此内容的一种方法是使用克拉克表示法:也就是说,您编写 {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 文档访问前缀。很少需要使用这些。
XPath is (deliberately) not designed for for the case where you want to use the same XPath expression for some unknown namespaces that only live in the XML document. You are expected to know the namespace ahead of time, declare the namespace to the XPath processor, and use the name in your expression. The answers by Martin and Dan show how to do this in C#.
The reason for this difficulty is best expressed in the XML namespaces spec:
That is, namespaces are supposed to be used to make sure you know what your document is talking about: is that
<head>
element talking about the preamble to an XHTML document or somebodies head in an AnatomyML document? You are never "supposed" to be agnostic about the namespace and it's pretty much the first thing you ought to define in any XML vocabulary.It should be possible to do what you want, but I don't think it can be done in a single XPath expression. First of all you need to rummage around in the document and extract all the namespaceURIs, then add these to the namespace manager and then run the actual XPath expression you want (and you need to know something about the distribution of namespaces in the document at this point, or you have a lot of expressions to run). I think you are probably best using something other than XPath (e.g. a DOM or SAX-like API) to find the namespaceURIs, but you could also explore the XPath namespace-axis (in XPath 1.0), use the
namespace-uri-from-QName
function (in XPath 2.0) or use expressions like Oleg's"configuration/*[local-name() = 'MyNode']"
. Anyway, I think your best bet is to try and avoid writing namespace agnostic XPath! Why do you not know your namespace ahead of time? How are you going to avoid matching things you don't intend to match?Edit - you know the namespaceURI?
So it turns out that your question confused us all. Apparently you know the namespace URI, but you don't know the namespace prefix that's used in the XML document. Indeed, in this case no namespace prefix is used and the URI becomes the default namspace where it is defined. The key thing to know is that the chosen prefix (or lack of a prefix) is irrelevant to your XPath expression (and XML parsing in general). The prefix / xmlns attribute is just one way to associate a node with a namespace URI when the document is expressed as text. You may want to take a look at this answer, where I try and clarify namespace prefixes.
You should try to think of the XML document in the same way the parser thinks of it - each node has a namespace URI and a local name. The namespace prefix / inheritance rules just saves typing the URI out lots of times. One way to write this down is in Clark notation: that is, you write {http://www.example.com/namespace/example}LocalNodeName, but this notation is usually just used for documentation - XPath knows nothing about this notation.
Instead, XPath uses its own namespace prefixes.Something like
/ns1:root/ns2:node
. But these are completely separate from and nothing to do with any prefixes that may be used in the original XML document. Any XPath implementation will have a way to map it's own prefixes with namespace URIs. For the C# implementation you use anXmlNamespaceManager
, in Perl you provide a hash, xmllint takes command line arguments... So all you need to do is create some arbitrary prefix for the namespace URI you know, and use this prefix in the XPath expression. It doesn't matter what prefix you use, in XML you just care about the combination of the URI and the localName.The other thing to remember (it's often a surprise) is that XPath doesn't do namespace inheritance. You need to add a prefix for every that has a namespace, irrespective of whether the namespace comes from inheritance, an xmlns attribute, or a namespace prefix. Also, although you should always think in terms of URIs and localNames, there are also ways to access the prefix from an XML document. It's rare to have to use these.
下面是如何使命名空间可用于中的 XPath 表达式的示例
XPathSelectElements 扩展方法:
Here's an example of how to make the namespace available to the XPath expression in the
XPathSelectElements extension method:
Xpath 2.0 + 库的示例:
using Wmhelp.XPath2;
doc.XPath2SelectElements("/*:configuration/*:MyNode");
请参阅:
适用于 .NET 的 XPath 和 XSLT 2.0?
Example with Xpath 2.0 + a library :
using Wmhelp.XPath2;
doc.XPath2SelectElements("/*:configuration/*:MyNode");
See :
XPath and XSLT 2.0 for .NET?
我很喜欢@mads-hansen,他的回答,所以我写了这些通用实用程序类成员:
I like @mads-hansen, his answer, so well that I wrote these general-purpose utility-class members: