ChatGPT解决这个技术问题 Extra ChatGPT

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

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

annakata 的解决方案有两个问题:1. 丑陋,2. 在这种情况下,它可以使用,但如果“ProjectGuid”元素属于多个命名空间并且我们只希望元素来自单个命名空间,则会提供错误的结果。使用 NamespaceManager 的解决方案更好
XPath 引擎必须提供正确的静态上下文,其中包含前缀和 NS URI 之间的绑定,以便在评估表达式时使用,否则您将无法引用命名空间内的内容。这就是@Teun 所做的。

a
annakata

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

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

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


同意,尽管当您必须深入几个级别时,这将成为真正的 PITA。不过,它确实有效。 :)
相当,这就是我选择 Barteks 的原因——唯一阻止我的是,如果我事先不知道命名空间或不能保证它,在这种情况下,我可能会先清洗整个文档,但是说所以只会让我跟踪者投反对票:)
这有两个问题:1. 丑陋,2. 在这种情况下,它可以使用,但如果“ProjectGuid”元素属于多个命名空间并且我们只希望元素来自单个命名空间,则会提供错误的结果。使用 NamespaceManager 的解决方案更好。
我对完全错误的东西投了反对票,而不是解决方案,尽管不是最好的
不错,这是不声明无聊且昂贵的命名空间的好方法
P
Peter

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

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


您不需要创建新的 XmlDocument 来获取 XmlNameTable。您可以使用 nsMgr = new XmlNamespaceManager(new NameTable());
啊,谢谢。我从来没有发现如何做到这一点。在 .NET 1.0 中是否已经可以使用 new NameTable()?
令人惊讶的是,从长远来看,从一开始就正确使用命名空间可以节省多少时间。
C
Community

此问题已出现在这里 several times already

您可以使用与命名空间无关的 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)

b
baretta

您只需注册此 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)

J
Jean-Marc Pannatier

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

......

L
Lonzo

为什么不使用 // 来忽略命名空间:

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

// 充当通配符,通过根和指定的下一个节点名称之间的所有内容(即 ProjectGuid)


实际上不起作用 - 是的,这表示在任何地方查找任何 ProjectGuid,但它仍然希望它们在默认命名空间中