SelectSingleNode returns null when tag contains xmlNamespace

C#XmlXmldocumentSelectsinglenodeSelectnodes

C# Problem Overview


I'm loading a string into an XML document that contains the following structure:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">                  
  <ItemGroup>
    <Compile Include="clsWorker.cs" />        
  </ItemGroup>      
</Project>

Then I'm loading all into an XmlDocument:

XmlDocument xmldoc = new XmlDocument();
xmldoc.LoadXml(Xml);

Then the following problem occurs:

XmlNode Node = xmldoc.SelectSingleNode("//Compile"); // returns null

When I remove the xmlns attribute from the root element (Project), it works fine.

How do I get SelectSingleNode to return the relevant element?

C# Solutions


Solution 1 - C#

You should use an XmlNamespaceManager in your call to SelectSingleNode():

XmlNamespaceManager ns = new XmlNamespaceManager(xmldoc.NameTable);
ns.AddNamespace("msbld", "http://schemas.microsoft.com/developer/msbuild/2003");
XmlNode node = xmldoc.SelectSingleNode("//msbld:Compile", ns);

Solution 2 - C#

Taken right from the documentation of SelectSingleNode() on the MSDN:

>Note
>If the XPath expression does not include a prefix, it is assumed that the >namespace URI is the empty namespace. If your XML includes a default >namespace, you must still add a prefix and namespace URI to the >XmlNamespaceManager; otherwise, you will not get a node selected. For >more information, see Select Nodes Using XPath Navigation.

And the immediately following sample code is

XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("ab", "http://www.lucernepublishing.com");
XmlNode book = doc.SelectSingleNode("//ab:book", nsmgr);

It's not as if this would be "hidden knowledge". ;-)

Solution 3 - C#

This way you don't need to specify namespace:

XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml("your xml");
XmlNode node = xmlDoc.SelectSingleNode("/*[local-name() = 'Compile']");
XmlNode nodeToImport = xmlDoc2.ImportNode(node, true);
xmlDoc2.AppendChild(nodeToImport);

Solution 4 - C#

Since the 'ItemGroup' may have multiple 'Compile' children, and you specifically want the 'Compile' children of 'Project/ItemGroup', the following will return all of the desired 'Compile' children and no others:

XmlDocument projectDoc = new XmlDocument();
projectDoc.Load(projectDocPath);
XmlNamespaceManager ns = new XmlNamespaceManager(projectDoc.NameTable);
ns.AddNamespace("msbld", "http://schemas.microsoft.com/developer/msbuild/2003");
XmlNodeList xnList = projectDoc.SelectNodes(@"/msbld:Project/msbld:ItemGroup/msbld:Compile", ns);

Note that the 'msbld:' namespace specification needs to precede each node level.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionShlomi KomemiView Question on Stackoverflow
Solution 1 - C#Frédéric HamidiView Answer on Stackoverflow
Solution 2 - C#TomalakView Answer on Stackoverflow
Solution 3 - C#Bruno BView Answer on Stackoverflow
Solution 4 - C#ValidView Answer on Stackoverflow