众所周知,XmlDocument可以进行XPath查询,但实际上这里所说的XPath查询仅限于没有命名空间(没有xmlns属性)的XML,一旦遇到有命名空间的XML,对应XPath查询都会无结果。

比如下面这个XML

a xmlns = "mgen.cnblogs.com" >

< b > ccc </ b >

XPath查询/a/b会返回null,而如果没有xmlns的话,会返回节点b。

为什么会这样呢?MSDN的相应函数有解释(参考: http://msdn.microsoft.com/en-us/library/system.xml.xmlnode.selectsinglenode.aspx

意思就是如果XPath表达式没有加前缀(如a:b中前缀是a),那么所查询节点(注意属性也可以是节点)的命名空间URI就应该是空值(也是默认值),否则XPath不会返回结果。

上面的XML, 因为节点a和b都有命名空间值,自然XPath查询不会有结果。

(上面英文还提到如果节点有默认命名空间,那么还得手动向XmlNamespaceManager添加前缀和命名空间值,这个在后面会讲的)

在看解决方案前,首先需要能够辨识XML命名空间,当然辨识XML命名空间值还是很容易的,参考如下XML(这个XML在后面程序中也会用到)

xml version =" 1.0 " encoding =" utf-8 "?>

< root xmlns =" dotnet " xmlns:w =" wpf ">

< a > data in a </ a >

< w:b > data in b </ w:b >

< c xmlns =" silverlight ">

< e > data in e </ e >

</ w:d >

</ root >

它的所有XML节点的命名空间如下所示:

xml version =" 1.0 " encoding =" utf-8 "?>

< root xmlns =" dotnet " xmlns:w =" wpf ">

<!-- xmlns: dotnet -->

< a > data in a </ a >

<!-- xmlns: dotnet -->

< w:b > data in b </ w:b >

<!-- xmlns: wpf -->

< c xmlns =" silverlight ">

<!-- xmlns: silverlight -->

<!-- xmlns: wpf -->

< e > data in e </ e >

<!-- xmlns: silverlight -->

</ w:d >

</ root >

如果识别XML命名空间没有问题,那么后面的操作就相当简单了,你需要记住: 在XmlDocument中用XPath查询某一节点时,只要它的命名空间值不是空值,那么你必须给它一个前缀 , 用这个前缀代表这个节点的命名空间值!这些前缀是通过XmlNamespaceManager类添加的,使用时将XmlNamespaceManager 传入SelectNodes或SelectSingleNode中即可。这也是为什么上面说“ 如果节点有默认命名空间,那么还得手动向 XmlNamespaceManager添加前缀和命名空间值”的原因。

另外构造一个XmlNamespaceManager需要XmlNameTable对象,这个对象可以从XmlDocument.NameTable和XmlReader.NameTable属性中得到。

下面我们步入代码,比如说查询上面XML中的节点e,分析位置节点e位于:root->c->d->e,然后将所需命名空间值加入到 XmlNamespaceManager中(前缀名称无所谓,只要在XPath一致即可),查询即可成功,如下代码:

* 假设上面XML文件在C:\a.txt中

* 下面代码会查询目标节点e,并输出数据:data in e

var xmlDoc = new XmlDocument ();

xmlDoc . Load( @"C:\a.txt" );

//加入命名空间和前缀

var xmlnsm = new XmlNamespaceManager (xmlDoc . NameTable);

xmlnsm . AddNamespace( "d" , "dotnet" );

xmlnsm . AddNamespace( "s" , "silverlight" );

xmlnsm . AddNamespace( "w" , "wpf" );

var node = xmlDoc . SelectSingleNode( "/d:root/s:c/w:d/s:e" , xmlnsm);

Console . WriteLine(node . InnerText);

//输出:data in e

编程是个人爱好