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?
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!
Two changes:
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.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);