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.