xml 具有命名空间的 XPath 选择节点

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/536441/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-06 12:21:47  来源:igfitidea点击:

XPath select node with namespace

xmlvb.netxpathxml-namespaces

提问by Peter

Its a .vbproj and looks like this

它是一个 .vbproj,看起来像这样

<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <PropertyGroup>
        <ProjectGuid>15a7ee82-9020-4fda-a7fb-85a61664692d</ProjectGuid>

all i want to get is the ProjectGuid but it does not work when a namespace is there...

我想要的只是 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")

what can i do to fix this?

我能做些什么来解决这个问题?

采纳答案by Teun D

The best way to do things like this (IMHO) is to create a namespace manager. This can be used calling SelectNodes to indicate which namespace URLs are connected to which prefixes. I normally set up a static property that returns an adequate instance like this (it's C#, you'll have to translate):

做这样的事情(恕我直言)的最好方法是创建一个命名空间管理器。这可用于调用 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;
  }
}

I include only one namespace here, but you could have multiple. Then you can select from the document like this:

我在这里只包含一个命名空间,但你可以有多个。然后你可以像这样从文档中选择:

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

Note that all of the elements are in the specified namespace.

请注意,所有元素都在指定的命名空间中。

回答by annakata

I'd probably be inclined to go with Bartek's* namespace solution, but a general xpath solution is:

我可能倾向于使用Bartek 的* 命名空间解决方案,但一般的 xpath 解决方案是:

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

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

**since Bartek's answer has disappeared, I recommend Teun's (which is actually more thorough)*

**由于 Bartek 的回答已经消失,我推荐 Teun 的(实际上更彻底)*

回答by Tomalak

This problem has been here severaltimesalready.

这个问题已经出现几次

Either you work with namespace-agnostic XPath expressions (not recommended for its clumsiness and the potential for false positive matches - <msb:ProjectGuid>and <foo:ProjectGuid>are the same for this expression):

要么你与命名空间无关的XPath表达式工作(不建议其笨拙和误报匹配的潜力-<msb:ProjectGuid>并且<foo:ProjectGuid>是相同的,该表达式):

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

or you do the right thing and use a XmlNamespaceManagerto register the namespace URI so you can include a namespace prefix in your XPath:

或者您做正确的事情并使用 aXmlNamespaceManager来注册名称空间 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)

回答by baretta

You need just to register this XML namespaces and associate with a prefix, to make the query work. Create and pass a namespace manager as second parameter when selecting the nodes:

您只需要注册这个 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)

回答by Jean-Marc Pannatier

One way is to use extensions + NameSpaceManager.
Code is in VB but is realy easy to translate to C#.

一种方法是使用扩展 + 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

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

......

回答by Lonzo

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)

// 作为通配符跟踪根节点和指定的下一个节点名称之间的所有内容(即 ProjectGuid)