Reputation: 38455
Its a .vbproj and looks like this
<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...
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?
Upvotes: 69
Views: 74375
Reputation: 31
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")
......
Upvotes: 0
Reputation: 338108
This problem has been here several times already.
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):
//*[local-name() = 'ProjectGuid']
or you do the right thing and use a XmlNamespaceManager
to register the namespace URI so you can include a namespace prefix in your 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)
Upvotes: 29
Reputation: 5099
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):
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.
Upvotes: 48
Reputation: 7585
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:
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)
Upvotes: 4
Reputation: 75794
I'd probably be inclined to go with Bartek's* namespace solution, but a general xpath solution is:
//*[local-name()='ProjectGuid']
**since Bartek's answer has disappeared, I recommend Teun's (which is actually more thorough)*
Upvotes: 68
Reputation: 2818
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)
Upvotes: -8