XPathselect具有名称空间的节点
它是一个.vbproj,看起来像这样
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <PropertyGroup> <ProjectGuid>15a7ee82-9020-4fda-a7fb-85a61664692d</ProjectGuid>
所有我想得到的是ProjectGuid,但它不起作用时,有一个命名空间…
Dim xmlDoc As New XmlDocument() Dim filePath As String = Path.Combine(mDirectory, name + "\" + name + ".vbproj") xmlDoc.Load(filePath) Dim value As Object = xmlDoc.SelectNodes("/Project/PropertyGroup/ProjectGuid")
我能做些什么来解决这个问题?
做这样的事情(恕我直言)最好的办法是创build一个名字空间pipe理器。 这可以用来调用SelectNodes来指出哪些名字空间的URL连接到了哪些前缀。 我通常设置一个静态属性,返回这样一个适当的实例(这是C#,你必须翻译):
private static XmlNamespaceManager _nsMgr; public static XmlNamespaceManager NsMgr { get { if (_nsMgr == null) { _nsMgr = new XmlNamespaceManager(new NameTable()); _nsMgr.AddNamespace("msb", "http://schemas.microsoft.com/developer/msbuild/2003"); } return _nsMgr; } }
我在这里只包含一个命名空间,但是可以有多个。 那么你可以select像这样的文件:
Dim value As Object = xmlDoc.SelectNodes("/msb:Project/msb:PropertyGroup/msb:ProjectGuid", NsMgr)
请注意,所有元素都在指定的名称空间中。
我可能会倾向于使用Bartek的 *名字空间解决scheme,但一般的xpath解决scheme是:
//*[local-name()='ProjectGuid']
**因为Bartek的答案已经消失了,所以我推荐Teun的(其实更彻底)*
这个问题已经有好 几次 了 。
要么你使用命名空间不可知的XPathexpression式(不build议使用它的笨拙和潜在的错误匹配 – <msb:ProjectGuid>
和<foo:ProjectGuid>
对于这个expression式是相同的):
// * [local-name()='ProjectGuid']
或者你做了正确的事情,并使用XmlNamespaceManager
来注册名称空间URI,以便在XPath中包含名称空间前缀:
Dim xmlDoc As New XmlDocument() xmlDoc.Load(Path.Combine(mDirectory, name, name + ".vbproj")) Dim nsmgr As New XmlNamespaceManager(xmlDoc.NameTable) nsmgr.AddNamespace("msb", "http://schemas.microsoft.com/developer/msbuild/2003") Dim xpath As String = "/msb:Project/msb:PropertyGroup/msb:ProjectGuid" Dim value As Object = xmlDoc.SelectNodes(xpath, nsmgr)
您只需要注册这个XML名称空间并将其与一个前缀相关联即可使查询正常工作。 select节点时,创build并传递名称空间pipe理器作为第二个参数:
Dim ns As New XmlNamespaceManager ( xmlDoc.NameTable ) ns.AddNamespace ( "msbuild", "http://schemas.microsoft.com/developer/msbuild/2003" ) Dim value As Object = xmlDoc.SelectNodes("/msbuild:Project/msbuild:PropertyGroup/msbuild:ProjectGuid", ns)
一种方法是使用扩展+ NameSpaceManager。
代码是在VB中,但是很容易翻译成C#。
Imports System.Xml Imports System.Runtime.CompilerServices Public Module Extensions_XmlHelper 'XmlDocument Extension for SelectSingleNode <Extension()> Public Function _SelectSingleNode(ByVal XmlDoc As XmlDocument, xpath As String) As XmlNode If XmlDoc Is Nothing Then Return Nothing Dim nsMgr As XmlNamespaceManager = GetDefaultXmlNamespaceManager(XmlDoc, "x") Return XmlDoc.SelectSingleNode(GetNewXPath(xpath, "x"), nsMgr) End Function 'XmlDocument Extension for SelectNodes <Extension()> Public Function _SelectNodes(ByVal XmlDoc As XmlDocument, xpath As String) As XmlNodeList If XmlDoc Is Nothing Then Return Nothing Dim nsMgr As XmlNamespaceManager = GetDefaultXmlNamespaceManager(XmlDoc, "x") Return XmlDoc.SelectNodes(GetNewXPath(xpath, "x"), nsMgr) End Function Private Function GetDefaultXmlNamespaceManager(ByVal XmlDoc As XmlDocument, DefaultNamespacePrefix As String) As XmlNamespaceManager Dim nsMgr As New XmlNamespaceManager(XmlDoc.NameTable) nsMgr.AddNamespace(DefaultNamespacePrefix, XmlDoc.DocumentElement.NamespaceURI) Return nsMgr End Function Private Function GetNewXPath(xpath As String, DefaultNamespacePrefix As String) As String 'Methode 1: The easy way Return xpath.Replace("/", "/" + DefaultNamespacePrefix + ":") ''Methode 2: Does not change the nodes with existing namespace prefix 'Dim Nodes() As String = xpath.Split("/"c) 'For i As Integer = 0 To Nodes.Length - 1 ' 'If xpath starts with "/", don't add DefaultNamespacePrefix to the first empty node (before "/") ' If String.IsNullOrEmpty(Nodes(i)) Then Continue For ' 'Ignore existing namespaces prefixes ' If Nodes(i).Contains(":"c) Then Continue For ' 'Add DefaultNamespacePrefix ' Nodes(i) = DefaultNamespacePrefix + ":" + Nodes(i) 'Next ''Create and return then new xpath 'Return String.Join("/", Nodes) End Function End Module
并使用它:
Imports Extensions_XmlHelper ...... Dim FileXMLTextReader As New XmlTextReader(".....") FileXMLTextReader.WhitespaceHandling = WhitespaceHandling.None Dim xmlDoc As XmlDocument = xmlDoc.Load(FileXMLTextReader) FileXMLTextReader.Close() ...... Dim MyNode As XmlNode = xmlDoc._SelectSingleNode("/Document/FirstLevelNode/SecondLevelNode") Dim MyNode As XmlNodeList = xmlDoc._SelectNodes("/Document/FirstLevelNode/SecondLevelNode") ......
为什么不使用//来忽略名称空间:
Dim value As Object = xmlDoc.SelectNodes("//ProjectGuid")
//作为通配符来跟踪指定的根节点和下一个节点名称之间的所有内容(即ProjectGuid)