2009-02-11 183 views
62

它是一种.vbproj和XPath选择节点看起来像这样命名空间

<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

两个问题:

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 

,并使用它1.它是丑陋的,在这种情况下,它可以使用,但如果是“ProjectGuid”元素将提供错误的结果属于多个命名空间,我们只需要来自单个命名空间的元素。使用NamespaceManager的解决方案更好 – 2009-02-11 14:36:58

+0

必须为XPath引擎提供正确的静态上下文,该静态上下文包含前缀和NS URI之间的绑定,以便在计算表达式时使用,否则将无法引用名称空间内的内容。这就是@Teun所做的。 – lkuty 2013-10-17 15:35:51

回答

43

做这样的事情(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) 

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

-7

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

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

//作为外卡通过指定(即ProjectGuid)的根和下一个节点名称

+7

实际上并没有工作 - 是的,这表示寻找任何ProjectGuids任何地方,但它仍然希望他们在默认命名空间 – annakata 2009-02-11 12:11:54

63

我之间的一切都遵循倒是可能会倾向于去 鲍尔泰克的 *命名空间的解决方案,但一般的XPath的解决方案是:

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

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

+0

同意,虽然这成为一个真正的PITA,当你不得不深入几个级别。不过,它*可以工作。 :) – ZombieSheep 2009-02-11 12:08:36

27

这个问题一直在这里severaltimesalready

要么你与命名空间无关的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

你只需要注册这个XML命名空间和associ吃了一个前缀,使查询工作。选择节点时 创建并传递一个命名空间管理器作为第二个参数:

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

的一种方法是使用扩展+ NameSpaceManager。
代码在VB中,但很容易转换为C#。与annakata的解决方案

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

......