Nice programing

네임 스페이스가있는 XPath 선택 노드

nicepro 2020. 11. 20. 09:37
반응형

네임 스페이스가있는 XPath 선택 노드


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

이 문제를 해결하려면 어떻게해야합니까?


이와 같은 작업을 수행하는 가장 좋은 방법 (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)

모든 요소는 지정된 네임 스페이스에 있습니다.


아마도 Bartek의 * 네임 스페이스 솔루션을 사용하는 경향이 있지만 일반적인 xpath 솔루션은 다음과 같습니다.

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

** Bartek의 답변이 사라졌기 때문에 Teun의 답변을 추천합니다 (실제로 더 철저 함) *


이 문제는 이미 여러 여기에있었습니다 .

어느 쪽이든 당신은 공간에 독립적 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)

쿼리가 작동하도록하려면이 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)

One way is to use extensions + NameSpaceManager.
Code is in VB but is realy easy to translate to 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

And to use it:

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

......

Why not use the // to ignore the namespace:

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

// acts as wild card to follow through everything between the root and the next node name specified(i.e ProjectGuid)

참고URL : https://stackoverflow.com/questions/536441/xpath-select-node-with-namespace

반응형