Zwei Web.config-XML-Dateien zusammenführen

Im Rahmen eines Setup-Builds musste ich zwei XML-Web.config-Dateien zu einer zusammen führen.

Nachfolgend der von mir selbst geschriebene Programmcode:

namespace XmlDocumentMerger
{
    using System;
    using System.Linq;
    using System.Text;
    using System.Xml;

    public static class XmlDocumentMergeController
    {
        public static string MergeDocuments(
            string xmlToMergeFrom,
            string xmlToMergeInto)
        {
            if (string.IsNullOrEmpty(xmlToMergeFrom)) return xmlToMergeInto;
            else if (string.IsNullOrEmpty(xmlToMergeInto)) return xmlToMergeFrom;
            else if (string.IsNullOrEmpty(xmlToMergeFrom) &&
                     string.IsNullOrEmpty(xmlToMergeInto)) return xmlToMergeInto;

            // --

            var sourceDoc = new XmlDocument();
            var destDoc = new XmlDocument();

            sourceDoc.LoadXml(xmlToMergeFrom);
            destDoc.LoadXml(xmlToMergeInto);

            // --

            foreach (
                var sourceNode in sourceDoc.ChildNodes.Cast<XmlNode>().Where(n => n.NodeType == XmlNodeType.Element))
            {
                doProcessNode(sourceNode, destDoc);
            }

            // --

            return destDoc.OuterXml;
        }

        private static void doProcessNode(XmlNode sourceNode, XmlNode destParentNode)
        {
            var destNode = findNode(sourceNode, destParentNode.OwnerDocumentIntelligent());
            if (destNode == null)
            {
                // Gibt es noch nicht, einfach rüber kopieren.
                copyNode(sourceNode, destParentNode);
            }
            else
            {
                // Gibt es schon, Inhalt verarbeiten.
                foreach (
                    var childNode in sourceNode.ChildNodes.Cast<XmlNode>().Where(n => n.NodeType == XmlNodeType.Element)
                    )
                {
                    doProcessNode(childNode, destNode);
                }
            }
        }

        private static void copyNode(XmlNode sourceNode, XmlNode destParentNode)
        {
            // ReSharper disable once PossibleNullReferenceException
            var newNode = destParentNode.OwnerDocumentIntelligent().ImportNode(sourceNode, true);
            destParentNode.AppendChild(newNode);
        }

        private static XmlNode findNode(XmlNode sourceNode, XmlNode destDoc)
        {
            var xPath = findXPath(sourceNode);
            var destNode = destDoc.SelectSingleNode(xPath);

            return destNode;
        }

        // http://stackoverflow.com/a/241291/107625
        private static string findXPath(XmlNode node)
        {
            var builder = new StringBuilder();
            while (node != null)
            {
                switch (node.NodeType)
                {
                    case XmlNodeType.Attribute:
                        builder.Insert(0, string.Format(@"/@{0}", node.Name));
                        node = ((XmlAttribute) node).OwnerElement;
                        break;
                    case XmlNodeType.Element:
                        var index = findElementIndex((XmlElement) node);
                        builder.Insert(0, string.Format(@"/{0}[{1}]", node.Name, index));
                        node = node.ParentNode;
                        break;
                    case XmlNodeType.Document:
                        return builder.ToString();
                    default:
                        throw new ArgumentException("Only elements and attributes are supported");
                }
            }
            throw new ArgumentException("Node was not in a document");
        }

        private static int findElementIndex(XmlNode element)
        {
            var parentNode = element.ParentNode;
            if (parentNode is XmlDocument)
            {
                return 1;
            }

            var parent = (XmlElement) parentNode;
            var index = 1;

            if (parent != null)
            {
                foreach (XmlNode candidate in parent.ChildNodes)
                {
                    if (candidate is XmlElement && candidate.Name == element.Name)
                    {
                        if (candidate == element)
                        {
                            return index;
                        }
                        index++;
                    }
                }
            }
            throw new ArgumentException("Couldn't find element within parent");
        }
    }

    internal static class XmlExtensions
    {
        public static XmlDocument OwnerDocumentIntelligent(this XmlNode node)
        {
            if (node == null) return null;
            else
            {
                var document = node as XmlDocument;
                return document ?? node.OwnerDocument;
            }
        }
    }
}

Sicher nicht 100% bullet-proof, dafür für mich „gut ist gut genug“ und ausreichend für das Setup.

Hier der Quellcode auf Pastebin. Und am Ende habe ich noch den Configuration File Merger gefunden.

Das geht so ähnlich auch mit JSON:

"Merging JSON".

Auszug von der Seite:

JObject o1 = JObject.Parse(@"{
  'FirstName': 'John',
  'LastName': 'Smith',
  'Enabled': false,
  'Roles': [ 'User' ]
}");

JObject o2 = JObject.Parse(@"{
  'Enabled': true,
  'Roles': [ 'User', 'Admin' ]
}");

o1.Merge(o2, new JsonMergeSettings
{
    // union array values together to avoid duplicates
    MergeArrayHandling = MergeArrayHandling.Union
});

string json = o1.ToString();
// {  "FirstName": "John",
//   "LastName": "Smith",
//   "Enabled": true,
//   "Roles": [
//     "User",
//     "Admin"
//   ]  }