Cloning XML Nodes from one Document into Another

Microsoft’s .NET Framework was designed in part to offer a powerful architecture for working with the Web. In the early 2000’s, when .NET was first being designed, that meant having strong support for handling and manipulating XML. Visual Basic.NET even includes support for XML literals. The result of this need was the System.XML Namespace, which contains all the methods and objects you’ll need to manage an XML document.

Almost. First, System.XML is kind of a pain to use, but that’s mostly because parsing XML with a statically typed platform is pretty rough, and frankly, the only languages where working with XML is usually even reasonable are dynamic, unless the XML format is very, very strongly defined, such as in SOAP.

System.XML, however, has no built in mechanism for cloning an element from one document (or document fragment) into another. Attempts to do so (even to copy simple attributes) will always result in exceptions because the elements were derived from different documents.

Recently, I was writing a simple set of MSBuild tasks to do xml file replacement on arbitrary XML files after reading a fragment in from a different document. This was in response to a problem I had using the web.config file replacement tasks from the Web Deployment Projects. Namely, the web deployment project’s task works by using the System.Configuration tools to do the section replacement, and that was causing me problems when referencing a non-GAC assembly in one of the pieces I was replacing when doing a build to production. Their task needs the file to appear to be a valid web.config, and errors out if it thinks it’s found a problem. Mine doesn’t care what you’re replacing with, so long as it’s XML.

My first attempt, was something like this:

// This XML Doc won't be repeated, since the method signature won't change.
/// <summary>
/// Replaces a section of an XML document with the document fragment found in a given file
/// </summary>
/// <param name="document">The XmlDocument object to modify in place</param>
/// <param name="section">The XPath referencing an XmlElement (or set of elements) to replace</param>
/// The path to the file containing the Xml Fragment to replace with</param>
void ReplaceSection(XmlDocument document, string section, string filename) 
{
    var sections = document.SelectNodes(section);
    var fragment = new XmlDocument();
    fragment.Load(filename);

    foreach (var s in sections.Cast<XmlNode>())
    {
        document = s.ParentNode.ReplaceChild(fragment, s).OwnerDocument;
    }
}

As stated above, this is going to throw an exception, because the fragment comes from a different document than the document I’m trying to replace into. So, it because necessary to clone the fragment into a new node, but this is non-trivial.

void ReplaceSection(XmlDocument document, string section, string filename) 
{
    var sections = document.SelectNodes(section);
    var fragment = new XmlDocument();
    fragment.Load(filename);

    var newNode = document.CreateNode(fragment.FirstChild.NoteType, 
                                       fragment.FirstChild.Name, 
                                       fragment.FirstChild.NamespaceURI);
    newNode.InnerXml = fragment.FirstChild.InnerXml;

    foreach (var s in sections.Cast<XmlNode>())
    {
        document = s.ParentNode.ReplaceChild(newNode, s).OwnerDocument;
    }
}

XML Parsing engines are great, so that InnerXml property is a lifesaver, since otherwise I’d have to recursively clone the entire fragment tree in order to do the replacement.

Keen eyed observers will note that this is also incomplete. since it doesn’t take into account Attributes on the fragment element, and will in fact, no include them at all. XmlAttributes have the same weaknesses as XmlElements regarding your abililty to simply replace them with ease, so yet more code is required to clone them.

var newNode = document.CreateNode(fragment.FirstChild.NoteType, 
                                   fragment.FirstChild.Name, 
                                   fragment.FirstChild.NamespaceURI);
newNode.InnerXml = fragment.FirstChild.InnerXml;

foreach (XmlAttribute attribute in fragment.FirstChild.Attrbitues)
{
    var newAttribute = document.CreateAttribute(attribute.Name);
    newAttribute.Value = attribute.Value;
    newNode.Attributes.Append(newAttribute);
}

This is an awful lot of code to have to write simply to convert an Xml Fragment into a form that .NET will allow me to inject into a new document. But, now that it’s written, it can be pretty easily injected into a new static method you can use!

public static XmlElement CloneNodeIntoDocument(this XmlDocument doc, XmlDocument fragment)
{
    var newNode = document.CreateNode(fragment.FirstChild.NoteType, 
                                       fragment.FirstChild.Name, 
                                       fragment.FirstChild.NamespaceURI);
    newNode.InnerXml = fragment.FirstChild.InnerXml;

    foreach (XmlAttribute attribute in fragment.FirstChild.Attrbitues)
    {
        var newAttribute = document.CreateAttribute(attribute.Name);
        newAttribute.Value = attribute.Value;
        newNode.Attributes.Append(newAttribute);
    }
    return newNode;
}

As always, the code samples on my blog are BSD-Licensed, feel free to use and remix them, just give credit where due.