posted on Thursday, August 25, 2005 6:28 PM
by
johnwood
Avoiding the Hassle of XmlNamespaceManager
The XML DOM's handling of namespaces is an inconvenience at best. Anyone who's had to use XPath over an existing document in .Net will know the hassles they hit when they try to use XPaths that include namespace prefixes. The bottom line is that you need to manually tell the DOM about the prefixes and their associated URIs even though the information is usually defined already in the document you are loading.
Take a document that starts with something like: (angle brackets replaced with square brackets for formatting)
[my:root xmlns:my="http://somenamespaceuri.com"]
[my:root xmlns:my=http://somenamespaceuri.com]
[my:someElement]Hello[/my:someElement]
…
In order to load this document and run an XPath over it, you'll need to also create a namespace manager and add the 'my' namespace declaration. What makes this inconvenient is that the information is already in the XML document, but the .Net implementation of the XML DOM just ignores it. I suppose there are infrequent cases when the namespace declarations are somewhere other than the root, and may even be redefined within the document, making it difficult to locate. However most of the time this isn't the case, so I think there's something we can do to make it easier.
Various articles/documents on the web tell you to write some code like this to work around the problem.
XmlDocument doc = new XmlDocument( ) ;
doc.Load ( "myfile.xml") ;
XmlNamespaceManager nsmgr = new XmlNamespaceManager( doc.NameTable) ;
nsmgr.Add ( "my", "http://somenamespaceuri.com") ;
XmlNode result = doc.SelectSingleNode( "//my:someElement", nsmgr) ;
The problems with this is that a) it's duplication, the declarations are already in the XML, and b) it's not extensible - user supplied XPaths won't work if the document changes and new namespaces are added.
I've come up with a possible workaround. This snippet of code automatically generates the XmlNamespaceManager based on the xmlns attributes on the root of the document (assuming that's where they are). Unfortunately the .Net DOM doesn't let you query for the xmlns attributes through XPath, it complains that they're system attributes and can't be referenced. But there is a way to get at them manually.
This is the code:
public static XmlNamespaceManager CreateNsMgr( XmlDocument doc)
{
XmlNamespaceManager nsmgr = new XmlNamespaceManager( doc.NameTable) ;
foreach ( XmlAttribute attr in doc.SelectSingleNode( "/*") .Attributes)
if ( attr.Prefix == "xmlns") nsmgr.AddNamespace( attr.LocalName, attr.Value) ;
return nsmgr;
}
This lets you write code without having to manually add in each namespace. For example the example code would be rewritten as:
XmlDocument doc = new XmlDocument( ) ;
doc.Load ("myfile.xml") ;
XmlNode result = doc.SelectSingleNode ("//my:someElement", CreateNsMgr(doc) ) ;
Note that it does assume that the namespace declarations are on the root element.