XmlDocument的XPath操作

某个客户询问我关于SelectSingleNode和SelectNodes的XPath操作,问题如下:

【xml】

<root>
  <dsobject classname="Rendition">
    <contentelements>
      <contentelement>Tansion</contentelement>
    </contentelements>
  </dsobject>

  <dsobject classname="Rendition">
    <contentelements>
      <contentelement>Herry</contentelement>
    </contentelements>
  </dsobject>

  <dsobject classname="Rendition">
    <contentelements>
      <contentelement>Jack</contentelement>
    </contentelements>
  </dsobject>
</root>

现在他要求“筛选出所有名称为dsobject,classname等于Rendition”,输出其contentelement的数据。

他这样做:

[C#]

foreach (XmlNode item in document.SelectNodes("//dsobject[@classname='Rendition']"))
{
  XmlNode oneNode = item.SelectSingleNode("//contentelement");
  string inner = oneNode.InnerText;
}

[VB.NET]

For Each item As XmlNode In document.SelectNodes("//dsobject[@classname='Rendition']")
	Dim oneNode As XmlNode = item.SelectSingleNode("//contentelement")
	Dim inner As String = oneNode.InnerText
Next

粗看先是从xml文档全部选出classname属性值为Rendition的全部dsobject(外循环),内部再从每个dsobject节点内部筛选出contentelement节点并输出包含的数值。但是实际上输出的结果只是三个“Tansion”。

究其原因,是因为XPath语法的特殊性:凡是以“//”开头的,都是无条件以整个xml的根节点开始遍历的。因此item.SelectSingleNode不要误以为是接着上面的dsobject节点往下检索,而是从头开始的!因此遍历的时候总是遍历到第一个dsobject中的contentelement的内部数值。

同样地,凡是以“/”开头的,都是无条件人为指定从根节点开始的绝对路径搜索,与XmlNode上下文毫无关系(比如要查询第一个contentelement,用绝对路径甚至可以这样写):

[C#]

Console.WriteLine(document.SelectSingleNode("/root/dsobject/contentelements/contentelement").InnerText);
Console.WriteLine(document.DocumentElement.SelectSingleNode("/root/dsobject/contentelements/contentelement").InnerText);

[VB.NET]

Console.WriteLine(document.SelectSingleNode("/root/dsobject/contentelements/contentelement").InnerText)
Console.WriteLine(document.DocumentElement.SelectSingleNode("/root/dsobject/contentelements/contentelement").InnerText)

显然,以/开头无论XmlNode是什么,总是被忽略且从指定的root=>dsobject=>contentelements=>contentment这样的顺序开始检索(绝对路径)。

至于和上下文相关的,就是直接写名字;比如:

[C#]

Console.WriteLine(document.SelectSingleNode("root/dsobject/contentelements/contentelement").InnerText);
Console.WriteLine(document.DocumentElement.SelectSingleNode("dsobject/contentelements/contentelement").InnerText);

[VB.NET]

Console.WriteLine(document.SelectSingleNode("root/dsobject/contentelements/contentelement").InnerText)
Console.WriteLine(document.DocumentElement.SelectSingleNode("dsobject/contentelements/contentelement").InnerText

显然,document的根节点是“#document”(特殊节点,xml中不存在),那么相对它而言下面的节点是root/dsobject/contentelments/contentelement。不过document.DocumentElement本身就是root,因此第二句代码中root就不能再写了,不然会发生找不到节点(因为不存在root=>root=>……这样的节点)。

SelectNodes也符合这样的特点,这里就不再叙述了。

posted @ 2012-05-13 20:11  Serviceboy  阅读(1448)  评论(0编辑  收藏  举报