2011-04-28 157 views
29

为什么这个Xpath不能使用XDocument.XPathSelectElement?XPathSelectElement总是返回null

的Xpath:

//Plugin/UI[1]/PluginPageCategory[1]/Page[1]/Group[1]/CommandRef[2] 

XML

<Plugin xmlns="http://www.MyNamespace.ca/MyPath"> 
    <UI> 
    <PluginPageCategory> 
     <Page> 
     <Group> 
      <CommandRef> 
      <Images> 
      </Images> 
      </CommandRef> 
      <CommandRef> 
      <Images> 
      </Images> 
      </CommandRef> 
     </Group> 
     </Page> 
    </PluginPageCategory> 
    </UI> 
</Plugin> 

C#代码:

myXDocument.XPathSelectElement("//Plugin/UI[1]/PluginPageCategory[1]/Page[1]/Group[1]/CommandRef[2]", myXDocument.Root.CreateNavigator()); 
+4

xpath查询中没有名称空间信息,这可能是原因。试着缩小这个范围,删除XML上的命名空间,看看是否会得到你的结果? – Cumbayah 2011-04-28 13:30:14

+2

这可能是由于命名空间 - 检查从XML中删除它是否修复它,如果有,请创建一个NamespaceManager。 – 2011-04-28 13:31:17

+0

@Cumbayah:打我吧! – 2011-04-28 13:32:11

回答

24

当使用的命名空间,这些都必须在XPath查询也可使用。您的XPath查询仅适用于没有名称空间的元素(可以通过从XML中删除名称空间进行验证)。

这里是你展示如何创建和传递一个命名空间管理器的示例:

var xml = ... XML from your post ...; 

var xmlReader = XmlReader.Create(new StringReader(xml)); // Or whatever your source is, of course. 
var myXDocument = XDocument.Load(xmlReader); 
var namespaceManager = new XmlNamespaceManager(xmlReader.NameTable); // We now have a namespace manager that knows of the namespaces used in your document. 
namespaceManager.AddNamespace("prefix", "http://www.MyNamespace.ca/MyPath"); // We add an explicit prefix mapping for our query. 

var result = myXDocument.XPathSelectElement(
    "//prefix:Plugin/prefix:UI[1]/prefix:PluginPageCategory[1]/prefix:Page[1]/prefix:Group[1]/prefix:CommandRef[2]", 
    namespaceManager 
); // We use that prefix against the elements in the query. 

Console.WriteLine(result); // <CommandRef ...> element is printed. 

希望这有助于。

+42

什么可怕的代码。 XML命名空间是真正难看的东西。 – 2011-11-14 06:34:46

+0

如果您必须明确地添加前缀/ ns组合,那么从XmlReader加载NameTable的意义何在?我假设你这样做是因为你不知道文档用于所需名称空间的前缀。但是,这不就意味着没有必要用'NameTable'初始化'XmlNamespaceManager'吗? – crush 2018-02-28 19:51:15

-2

有一种方法可以在不改变xpath的情况下做到这一点。我找到的解决方案是在将XML解析到XDocument时删除名称空间。

这里是个例:

var regex = @"(xmlns:?[^=]*=[""][^""]*[""])"; 
var myXDocument = XDocument.Parse(Regex.Replace("MyXmlContent", regex, "", RegexOptions.IgnoreCase | RegexOptions.Multiline)) 

现在的命名空间没有了,这是easyer操纵。

+6

_“现在名称空间不见了”_您可能没有一个格式良好的XML文档... – 2011-04-29 04:47:06

+13

香肠工厂解决方案。 – 2011-04-29 06:21:18

+2

呃...它对我来说非常适合。请注意,当您无法更改xpath时它可能很有用。 – 2011-04-29 13:48:46

14

这应该可能是对@ Cumbayah的帖子的评论,但我似乎无法留下任何评论。

你可能更喜欢使用这样的东西,而不是使用XmlReader来获取名称表。

var xml = ... XML from your post ...; 
var myXDocument = XDocument.Parse(xml); 
var namespaceManager = new XmlNamespaceManager(new NameTable()); 
namespaceManager.AddNamespace("prefix", "http://www.MyNamespace.ca/MyPath"); 

var result = ...; 
+1

只是想指出缺省名称空间应加前缀的问题。即namespaceManager.AddNamespace(“”,“http://www.MyNamespace.ca/MyPath”)将使DefaultNamespace属性与预期一致,但它不会被XPathSelectElement使用。误导! – 2012-08-21 12:52:06

2

你的情况最简单的方法是使用XPath 节点测试节点名称和位置选择的元素。 您的XPath的选择:

myXDocument.XPathSelectElement("//Plugin/UI[1]/PluginPageCategory[1]/Page[1]/Group[1]/CommandRef[2]", myXDocument.Root.CreateNavigator()); 

可以很容易地转换成:

myXDocument.XPathSelectElement("/child::node()[local-name()='Plugin']/child::node()[local-name()='UI'][position()=1]/child::node()[local-name()='PluginPageCategory'][position()=1]/child::node()[local-name()='Page'][position()=1]/child::node()[local-name()='Group'][position()=1]/child::node()[local-name()='CommandRef'][position()=2]"); 

没有必要创建和传递的XmlNamespaceManager作为参数。

+1

太棒了,这个作品很棒。命名空间应该很简单,但非常棘手,所以这个解决方案非常有用。 – 2017-05-24 02:34:16