Search code examples
c#.netxmlxml-namespacesxmldocument

XmlNode.InnerXml property - omit xmlns attribute


I have some code to replace the root node name of an XML document while retaining its namespace.

XmlDocument doc = new XmlDocument();
Stream inStream = inmsg.BodyPart.GetOriginalDataStream();
doc.Load(inStream);

XmlNode root = doc.DocumentElement;
XmlNode replacement = doc.SelectSingleNode("/*/*[1]");

XmlNode newRoot = doc.CreateElement(replacement.Name);
XmlAttribute xmlns = (XmlAttribute)root.Attributes["xmlns"].Clone();
newRoot.Attributes.Append(xmlns);
newRoot.InnerXml = root.InnerXml; //the problem is here!

doc.ReplaceChild(newRoot, root);

With a document that begins like this:

<OLD_ROOT xmlns="http://my.xml.namespace">
    <NEW_ROOT>

It results in:

<NEW_ROOT xmlns="http://my.xml.namespace">
     <NEW_ROOT xmlns="http://my.xml.namespace">

The second xmlns is because the InnerXml property apparently sets it on the first node of its contents! What can I do to circumvene this, without having to remove it afterwards?

Cannot remove it afterwards:

Tried with the following code

XmlNode first_node = doc.SelectSingleNode("/*/*[1]");
XmlAttribute excess_xmlns = first_node.Attributes["xmlns"];
first_node.Attributes.Remove(excess_xmlns);

But this does not work as xmlns apparently does not exist as an attribute on that node!


Solution

  • Two changes:

    1. Instead of adding an xmlns attribute after you create newRoot, specify the namespace as the second argument in the call to doc.CreateElement. This ensures that the NamespaceURI property is set correctly.
    2. Instead of copying InnerXml (which results in redundant xmlns attributes), use AppendChild to move each child node one at a time. This will likely be more efficient anyway.

    Thus:

    XmlNode root = doc.DocumentElement;
    XmlNode replacement = doc.SelectSingleNode("/*/*[1]");
    
    XmlNode newRoot = doc.CreateElement(replacement.Name, replacement.NamespaceURI);
    while (root.ChildNodes.Count > 0)
        newRoot.AppendChild(root.FirstChild);
    
    doc.ReplaceChild(newRoot, root);