XPath selecciona nodo con espacio de nombre

Es un .vbproj y se ve así

  15a7ee82-9020-4fda-a7fb-85a61664692d 

todo lo que quiero obtener es el ProjectGuid, pero no funciona cuando hay un espacio de nombres …

  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") 

¿Qué puedo hacer para arreglar esto?

La mejor manera de hacer cosas como esta (en mi humilde opinión) es crear un administrador de espacio de nombres. Esto se puede usar llamando a SelectNodes para indicar qué URL de espacio de nombres están conectadas a qué prefijos. Normalmente configuro una propiedad estática que devuelve una instancia adecuada como esta (es C #, tendrás que traducir):

 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; } } 

Incluyo solo un espacio de nombre aquí, pero podría tener varios. Luego puede seleccionar desde el documento de esta manera:

 Dim value As Object = xmlDoc.SelectNodes("/msb:Project/msb:PropertyGroup/msb:ProjectGuid", NsMgr) 

Tenga en cuenta que todos los elementos están en el espacio de nombres especificado.

Probablemente me inclinaría por la solución de espacio de nombres * de Bartek , pero una solución general de xpath es:

//*[local-name()='ProjectGuid']

** dado que la respuesta de Bartek ha desaparecido, recomiendo la de Teun (que en realidad es más completa) *

Este problema ha estado aquí varias veces .

O trabajas con expresiones XPath agnósticas de espacios de nombres (no recomendadas por su torpeza y la posibilidad de coincidencias de falsos positivos: y son las mismas para esta expresión):

  // * [local-name () = 'ProjectGuid'] 

o hace lo correcto y utiliza un XmlNamespaceManager para registrar el URI de espacio de nombres para que pueda incluir un prefijo de espacio de nombres en su 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) 

Solo necesita registrar estos espacios de nombres XML y asociarlos con un prefijo para que la consulta funcione. Cree y pase un gestor de espacio de nombres como segundo parámetro al seleccionar los nodos:

 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) 

Una forma es usar extensiones + NameSpaceManager.
El código está en VB pero es realmente fácil de traducir a C #.

 Imports System.Xml Imports System.Runtime.CompilerServices Public Module Extensions_XmlHelper 'XmlDocument Extension for SelectSingleNode  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  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 

Y para usarlo:

 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") ...... 

¿Por qué no usar // para ignorar el espacio de nombres?

 Dim value As Object = xmlDoc.SelectNodes("//ProjectGuid") 

// actúa como comodín para seguir todo entre la raíz y el siguiente nombre de nodo especificado (es decir, ProjectGuid)