forge

XML, XPath, and Namespaces

One of the problems that I had early on with XML when I started was that I couldn’t figure out an easy way to handle namespaces when I was processing XML. SharePoint and other Microsoft technologies like InfoPath make extensive use of namespaces. For instance, if you right click a field in InfoPath and select Copy XPath you’ll get something in your clipboard that looks like: /my:TestForm/my:Repeating/my:Message –Frankly, that’s not all that complex of an XPath statement, except that it has a namespace in it. In this case we don’t know what “my” refers to. That made it easier for me to transform the XPath statement into /*[local-name()=’TestForm’/*[local-name()=’Repeating’]/*[local-name()=’Message’]. It’s frankly not that big a deal except that it’s somewhat tedious. This allows me to call XMLDocument.SelectNodes(string xPath) instead of XMLDocument.SelectNodes(string xPath, XmlNamespaceManager nsmgr) which was good because I didn’t have a namespace manager and I didn’t know how to create one easily. However, I ran into a new problem that my quick solution didn’t allow me to get around. I wanted to add a set of new nodes to an existing document — in that case I couldn’t ignore namespaces any longer.

After a bit of gnashing of teeth I realized that I can create a XmlNameSpaceManager pretty easily by looking at the attributes of my document element (DocumentElement) node. Take a look at this code:

XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);

foreach (XmlAttribute attr in doc.DocumentElement.Attributes)

{

if (attr.Name.IndexOf(“xmlns:”) == 0)

Help Your SharePoint User

{

string prefix = attr.Name.Replace(“xmlns:”, “”);

string nsUri = attr.Value;

nsmgr.AddNamespace(prefix, nsUri);

}

}

In this snippet I use the document element attributes to create my namespace manager — thus I can use the same namespace prefixes as are in use in the document itself. (Forgive me for using .Replace() rather than .Substring() – I ultimately decided it was more readable.) Taking this scenario a bit farther, with the namespace manager I can create a node of <my:Message /> with:

XmlElement ele = doc.CreateElement(“my”, “Message”, nsmgr.LookupNamespace(“my”));

This abstracts out what the namespace actually is so that I can just reference the existing prefixes. With the element created I can set its InnerText property and then use XmlNode.AppendChild() to append the new element into my document.

Since it’s easy to create XmlNameSpaceManager objects now … I might have to forget my *[local-name()=’foo’] trick… although it’s still useful when I’m leveraging Xml editing tools and I don’t have namespace support in them.