XPath выберите узел с пространством имен - PullRequest
64 голосов
/ 11 февраля 2009

Это .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")

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

Ответы [ 6 ]

63 голосов
/ 11 февраля 2009

Я, вероятно, был бы склонен использовать решение Бартека * для пространства имен, но общее решение xpath:

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

**, так как ответ Бартека исчез, я рекомендую ответ Теуна (который на самом деле более тщательный) *

44 голосов
/ 11 февраля 2009

Лучший способ сделать такие вещи (IMHO) - создать менеджер пространства имен. Это можно использовать, вызывая 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;
  }
}

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

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

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

27 голосов
/ 11 февраля 2009

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

Либо вы работаете с независимыми от пространства имен выражениями 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)
3 голосов
/ 11 февраля 2009

Вам нужно просто зарегистрировать это пространство имен 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)
0 голосов
/ 11 апреля 2016

Одним из способов является использование расширений + 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")

......
0 голосов
/ 11 февраля 2009

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

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

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

...