在任意深度上通过XPath选择一个XML节点

4

我有一个XML文档,想要获取第一个具有特定名称的节点,无论它在哪个嵌套深度中。

我尝试了几种方法,但都没有成功:

var node1 = doc.SelectSingleNode(@"//Shortcut");
var node2 = doc.SelectSingleNode(@"/*/Shortcut");
var node3 = doc.SelectSingleNode(@"//*/Shortcut");
var node4 = doc.SelectSingleNode(@"*/Shortcut");
...

每次调用都会生成一个NULL节点。
我认为这应该是一些琐碎的XPath语法。你能帮我吗?
(如果有关系的话:XML文档是WiX项目的输入文件,所以可能涉及一些命名空间问题?!)。 编辑 我也尝试了以下内容:
var nsm = new XmlNamespaceManager(doc.NameTable);
nsm.AddNamespace(string.Empty, @"http://schemas.microsoft.com/wix/2006/wi");
nsm.AddNamespace(@"ns", @"http://schemas.microsoft.com/wix/2006/wi");

一起:

var node1 = doc.SelectSingleNode(@"//Shortcut", nsm);
var node2 = doc.SelectSingleNode(@"/*/Shortcut", nsm);
var node3 = doc.SelectSingleNode(@"//*/Shortcut", nsm);
var node4 = doc.SelectSingleNode(@"*/Shortcut", nsm);
...

Leading to the same results.(导致相同的结果。)
编辑2 - 解决方案
我找到了解决方案:
var nsm = new XmlNamespaceManager(doc.NameTable);
nsm.AddNamespace(string.Empty, @"http://schemas.microsoft.com/wix/2006/wi");
nsm.AddNamespace(@"ns", @"http://schemas.microsoft.com/wix/2006/wi");

然后

var node1 = doc.SelectSingleNode(@"//ns:Shortcut", nsm);

这成功了。


1
好问题,+1。请看我的答案,其中提供了更一般和灵活的解决方案。 - Dimitre Novatchev
3个回答

2

选择准确的节点(无多余内容)的XPath表达式为:

(//x:Shortcut)[1]

因此,使用:

doc.SelectNodes("(//x:Shortcut)[1]", someNamespaceManager)

someNamespaceManager 中,前缀 "x" 绑定到命名空间 "http://schemas.microsoft.com/wix/2006/wi"

这种方法比使用 SelectSingleNode() 的提议更有优势,因为它可以很容易地调整以选择 XML 文档中所需的第 N 个节点。

例如:

   (//x:Shortcut)[3]

选择文档中第三个x:Shortcut元素,并且

   (//x:Shortcut)[last()]

选择XML文档中最后一个(按文档顺序)x:Shortcut元素。


2
我确实理解XPath表达式的改进,但我不明白为什么你建议使用SelectNodes而不是SelectSingleNode。显然,如果路径表达式被编写为使用位置谓词选择单个节点,那么你同样可以将其传递给SelectSingleNode - Martin Honnen
@Martin Honnen: SelectSingleNode() 帮助 OP 选择 //someName 中的第一个节点 -- 它总是只选择第一个节点。我想向他展示,他不需要使用 SelectSingleNode() 的这个特性,因为他可以在 XPath 表达式本身中指定他需要哪个节点或节点。然后,他将不必(被迫)使用 SelectSingleNode()。我并不是说他不能使用 SelectSingleNode() -- 只是他现在可以自由选择不使用它。 - Dimitre Novatchev

2
为什么不使用 XDocument
XDocument doc = XDocument.Load("test.xml");
doc.Descendants("Shortcut").First();

IMO XDocument比XPath更快且更易读。


谢谢。XDocument是什么类型的类?来自自定义类吗? - Uwe Keim
1
@Uwe Keim,我已经添加了参考链接。 - Saeed Amiri
1
@Uwe Keim:这是Linq-to-XML的一部分,最初在.NET 3.0(VS 2008)中引入。 - marc_s

2

我终于自己找到了解决方案:

var nsm = new XmlNamespaceManager(doc.NameTable);
nsm.AddNamespace(string.Empty, @"http://schemas.microsoft.com/wix/2006/wi");
nsm.AddNamespace(@"ns", @"http://schemas.microsoft.com/wix/2006/wi");

然后

var node1 = doc.SelectSingleNode(@"//ns:Shortcut", nsm);

这个成功了。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接