XML is undoubtedly one of the most powerful and yet, in many ways, over-rated technologies to come along in quite some time.  I'm not trying to make light of the impact it has had ... I work in BizTalk Server alot, I can hardly make light of XML, but at the end of the day it's just not that hard to be a “XML expert”.

In my opinion though, namespaces are definitely the black belt test of understanding XML.  Not because the markup itself is tremendosly hard to work with, but rather because dealing with XML in a programmatic way once namespaces become involved is overly difficult.

The .NET Framework answer is of course to use the XmlNamespaceManager.  This object acts as a collection of namespace URIs and prefixes you wish to use.  Go given a piece of XML which looks like this:

<t:MyRoot xmlns:t="http://www.junk.edu/">
<t:SomeNode>
<w:AnotherNode xmlns:t="http://www.foo.edu/">Data Needs Replacing </w:AnotherNode>
</t:SomeNode>
</t:MyRoot>

Getting at this with the XmlDocument object is not exactly straight forward.  Most people who are new to XML would simply load this data and assume that SelectSingleNode(”/t:MyRoot/t:SomeNode/w:AnotherNode”) would return the w:AnotherNode XmlNode object.  This is not correct because the XmlDocument object is agnostic to Namespaces.  It believes Namespaces exist (hence why XmlNode has a Namespace property) but the XPath parsers do not by default know what namespaces are at play in the document.  Enter the XmlNamespaceManager, which allows you to define how you want to refer to any namespace.

In order to get at the data which needs replacing above (the value of w:AnotherNode) we will need to use an XmlNamespaceManager to make clear to the XPath parsers what we will be calling each of the namespaces.  Let's assume that the function below is called and given an XmlDocument already loaded with data, and the new value we want for w:AnotherNode.

private void SetAnotherNode(XmlDocument doc, string newValue)
{
     XmlNamespaceManager nm = new XmlNamespaceManager(doc.NameTable);

     nm.AddNamespace(“junk“,“http://www.junk.edu/“);
     nm.AddNamespace(“foo“,“http://www.foo.edu/“);

     XmlNode n = doc.SelectSingleNode       (“/junk:MyRoot/junk:SomeNode/foo:AnotherNode“,nm);
     n.InnerText = newValue;
}

This code creates an XmlNamespaceManager object, which requires the NameTable from the document you are searching in order to be created.  It then adds to that XmlNamespaceManager two prefixes and URIs.  In this case, you will note we add the two namespaces we need to reference, and we give them prefixes of our choosing.  Note this does not need to match the prefix being used in the document because the URI is used to match to the document, not the prefix. Next we do the SelectSingleNode, but this time we use our prefixes (junk and foo) instead of the document prefixes (t and w).  Also very important is that we use the overload of SelectSingleNode which takes an XmlNamespaceManager as a parameter and give it the object (nm) which we created.  Finally we just assign the value of the node as we would normally do.

Important things to keep in mind when dealing with the XmlNamespaceManager.

  1. Not every namespace that has been added via .AddNamespace must exist in the document you are working with.
  2. If you are dealing with namespaces in multiple components, it would be very helpful to those who come later if there was a uniform namespace prefix scheme throughout all of your components.  While it is perfectly possible to refer to the http://www.foo.edu/ URI as 'foo' in one component and 'crash' in another and 'dog' in a third, this simply makes your system more difficult to understand and makes it impossible to share XPath statements between these components.

Within the next few days I hope to be posting an article about one possible way to manage namespaces within your organization such that uniform prefixes just happen, and it lowers the amount of code which developers have to re-write over and over again (always a good thing).  Look for the announcement soon.