XPath выберите node с пространством имен

Его .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")

что я могу сделать, чтобы исправить это?

Ответ 1

Лучший способ сделать такие вещи (IMHO) - создать диспетчер пространства имен. Это можно использовать, вызывая SelectNodes, чтобы указать, какие URL-адреса пространства имен связаны с префиксами. Обычно я устанавливаю статическое свойство, которое возвращает соответствующий экземпляр (это С#, вам придется переводить):

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

Здесь я включаю только одно пространство имен, но у вас может быть несколько. Затем вы можете выбрать из документа, как это:

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

Обратите внимание, что все элементы находятся в указанном пространстве имен.

Ответ 2

Я бы, вероятно, склонялся к решению пространства имен Bartek *, но общее решение xpath:

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

**, так как ответ Бартэка исчез, я рекомендую Teun (что на самом деле более подробно) *

Ответ 3

Эта проблема была здесь несколько раз уже.

Либо вы работаете с выражениями XPath без выражения (не рекомендуется для его неуклюжести и потенциал для ложноположительных совпадений - <msb:ProjectGuid> и <foo:ProjectGuid> одинаковы для этого выражения):

//*[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)

Ответ 4

Вам нужно просто зарегистрировать эти пространства имен XML и связать с префиксом, чтобы заставить запрос работать. Создайте и передайте менеджер пространства имен в качестве второго параметра при выборе узлов:

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)

Ответ 5

Один из способов - использовать расширения + NameSpaceManager.
Код находится в VB, но очень легко перевести на С#.

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

......

Ответ 6

Почему бы не использовать//для игнорирования пространства имен:

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

//действует как дикая карта, чтобы следить за всем, что указано между именем root и следующим node (например ProjectGuid)