Xml-SelectNodes with default-namespace via XmlNamespaceManager not working as expected

.net, c#, unit-testing, xpath

Solution

// This will fail with dotnet 3.5sp1. Why? 
//!!!! 
Assert.AreEqual(2, doc.SelectNodes("//b", nsmgr).Count);

This is a FAQ. In XPath any unprefixed name is assumed to be in "no namespace". In order to select elements that belong to a namespace, in any XPath expression their names must be prefixed with a prefix that is associated with this namespace. The `AddNamespace()` method serves exactly this purpose. It creates a binding between a specific namespace and a specific prefix. Then, if this prefix is used in an XPath expression, the element prefixed by it can be selected.

It is written in the XPath W3C spec: "A QName in the node test is expanded into an expanded-name using the namespace declarations from the expression context. This is the same way expansion is done for element type names in start and end-tags except that the default namespace declared with xmlns is not used: if the QName does not have a prefix, then the namespace URI is null".

See this at: w3.org/TR/xpath/#node-tests .

So, any unprefixed name is considered to be in "no namespace". In the provided XML document there are no `b` elements in "no namespace" and this is why the XPath expression `//b` selects no nodes at all.

Use:

XmlNamespaceManager nsmanager = new XmlNamespaceManager(doc.NameTable);
nsmanager.AddNamespace("x", "urn:test.Schema");

and later:

Assert.AreEqual(2, doc.SelectNodes("//x:b", nsmanager).Count);

Remember: The whole purpose of registering the namespace is to be able to use the prefix (in this case `x`) in any XPath expression.

Problem

I have some xml with default namespace ``` <a xmlns='urn:test.Schema'><b/><b/></a> ``` and want to count the number of `<b/>` How do I have to define ``` XmlNamespaceManager nsmgr = ???? Assert.AreEqual(2, doc.SelectNodes("//b", nsmgr).Count); ``` so that the assert becomes true? I have tried so far (using nunit): ``` [Test] [Ignore("Why does this not work?")] public void __DoesNotWork_TestSelectWithDefaultNamespace() { // xml to parse with defaultnamespace string xml = @"<a xmlns='urn:test.Schema'><b/><b/></a>"; XmlDocument doc = new XmlDocument(); doc.LoadXml(xml); // fails because xpath does not have the namespace //!!!! Assert.AreEqual(2, doc.SelectNodes("//b").Count); // using XPath defaultnamespace XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable); nsmgr.AddNamespace("", "urn:test.Schema"); // This will fail with dotnet 3.5sp1. Why? //!!!! Assert.AreEqual(2, doc.SelectNodes("//b", nsmgr).Count); } [Test] public void TestSelectWithoutNamespaces_Ok() { // xml to parse without namespace string xml = @"<a><b/><b/></a>"; XmlDocument doc = new XmlDocument(); doc.LoadXml(xml); // works ok Assert.AreEqual(2, doc.SelectNodes("//b").Count); // works ok XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable); Assert.AreEqual(2, doc.SelectNodes("//b", nsmgr).Count); } [Test] public void TestSelectWithNamespacesPrefixed_Ok() { // xml to parse with defaultnamespace string xml = @"<a xmlns='urn:test.Schema'><b/><b/></a>"; XmlDocument doc = new XmlDocument(); doc.LoadXml(xml); // using XPath namespace via alias "t". works ok but xpath is to complicated XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable); nsmgr.AddNamespace("t", doc.DocumentElement.NamespaceURI); Assert.AreEqual(2, doc.SelectNodes("//t:b", nsmgr).Count); } ```

Original source