使用XmlDocument.SelectNodes无法选中XML节点的问题排查与解决
解决XmlDocument.SelectNodes返回0的问题:处理XML默认命名空间
最近我碰到一个头疼的问题:想用VB.NET的XmlDocument.Select相关方法从下面的XML文件里提取指定节点的值,结果不管是用descendant::fields这种宽泛的XPath,还是更精准的路径,nodeList.Count始终返回0,根本拿不到想要的节点。
目标XML文件
<?xml version="1.0"?> <cqresponse xmlns="http://ibm.com/rational/clearquest/web/v7.1"> <displayname>hello world</displayname> <fields> <field> <fieldname>Defect_details_linenumber</fieldname> <datatype>MULTILINE_STRING</datatype> <value><![CDATA[["aaa"]]]></value> </field> <field> <fieldname>Defect_detailsAf_artefs</fieldname> <datatype>MULTILINE_STRING</datatype> <value><![CDATA[["bbb"]]]></value> </field> </fields> </cqresponse>
最初的错误代码
Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click Dim doc As XmlDocument = New XmlDocument() doc.Load("Temp_File.xml") Dim nodeList As XmlNodeList Dim root As XmlNode = doc.DocumentElement nodeList = root.SelectNodes("descendant::fields[field/fieldname='Defect_detailsAf_artefs']") Debug.Print(nodeList.Count) Debug.Print(nodeList(0).InnerText) End Sub
问题原因
排查了好久才发现,问题出在XML的默认命名空间上!这个XML的根节点cqresponse带有xmlns="http://ibm.com/rational/clearquest/web/v7.1"属性,这意味着所有子节点都默认属于这个命名空间。但XmlDocument的XPath查询默认不会自动识别这个命名空间,直接写节点名称的话,XPath会去查找没有命名空间的节点,自然匹配不到。
解决方法:处理命名空间
要解决这个问题,我们需要用XmlNamespaceManager来绑定命名空间前缀,然后在XPath表达式中通过前缀指定节点所属的命名空间。修改后的代码如下:
Private Sub Button4_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button4.Click Dim doc As XmlDocument = New XmlDocument() doc.Load("Temp_File.xml") ' 绑定XML的默认命名空间,自定义前缀为ibm Dim ns As XmlNamespaceManager = New XmlNamespaceManager(doc.NameTable) ns.AddNamespace("ibm", "http://ibm.com/rational/clearquest/web/v7.1") ' 获取根节点 Dim root As XmlNode = doc.DocumentElement ' 使用带前缀的XPath精准定位目标节点 Dim node As XmlNode = root.SelectSingleNode("descendant::ibm:fields/ibm:field[ibm:fieldname='Defect_detailsAf_artefs']/ibm:value", ns) Debug.Print(node.InnerText) ' 这里会输出预期的"bbb" End Sub
关键说明
- 第一步创建
XmlNamespaceManager,通过AddNamespace给XML的默认命名空间绑定一个前缀(前缀名称可以自己定,比如这里的ibm) - 编写XPath时,每个节点名称前都要加上这个前缀,告诉XPath这些节点属于我们绑定的命名空间
- 调用
SelectSingleNode或SelectNodes时,一定要把XmlNamespaceManager作为第二个参数传入,这样查询才能正确识别命名空间下的节点
内容的提问来源于stack exchange,提问作者Bruce
相关产品推荐
相关产品推荐

