views:

2451

answers:

3

I wrote this piece of code to merge two .NET configuration files: Add non existent nodes and override values on existing ones. I avoided to use the much faster reader/writer way because the app I'm using it for is not performance critical. What do you think of this?

using System;
using System.Xml;

namespace devcoach.FrameworkExtensions
{
    /// <summary>
    /// Extension methods for the type 
    /// <see cref="System.Xml.XmlDocument"/>.
    /// </summary>
    public static class XmlDocumentExtensions
    {
        /// <summary>
        /// Merges the specified instance.
        /// </summary>
        /// <param name="instance">The instance.</param>
        /// <param name="mergeDoc">The merge doc.</param>
        public static void Merge(
            this XmlDocument instance,
            XmlDocument mergeDoc)
        {
            var mergeRoot = mergeDoc.DocumentElement;
            var sourceRoot = instance.DocumentElement;
            if (sourceRoot == null) return;
            instance.Merge(sourceRoot, mergeRoot, false);
        }

        /// <summary>
        /// Merges the specified instance.
        /// </summary>
        /// <param name="instance">The instance.</param>
        /// <param name="mergeDoc">The merge doc.</param>
        /// <param name="isNetConfigFile">if set to <c>true</c> if documents 
        /// are .net config files.</param>
        public static void Merge(
            this XmlDocument instance,
            XmlDocument mergeDoc,
            bool isNetConfigFile)
        {
            var mergeRoot = mergeDoc.DocumentElement;
            var sourceRoot = instance.DocumentElement;
            if (sourceRoot == null) return;
            instance.Merge(sourceRoot, mergeRoot, true);
        }

        /// <summary>
        /// Merges the specified source doc.
        /// </summary>
        /// <param name="sourceDoc">The source doc.</param>
        /// <param name="sourceRoot">The source root.</param>
        /// <param name="mergeRoot">The merge root.</param>
        public static void Merge(
            this XmlDocument sourceDoc,
            XmlNode sourceRoot,
            XmlNode mergeRoot)
        {
            sourceDoc.Merge(
                sourceRoot,
                mergeRoot,
                false);
        }

        /// <summary>
        /// Merges the specified source doc.
        /// </summary>
        /// <param name="sourceDoc">The source doc.</param>
        /// <param name="sourceRoot">The source root.</param>
        /// <param name="mergeRoot">The merge root.</param>
        /// <param name="isNetConfigFile">if set to <c>true</c> if documents 
        /// are .net config files.</param>
        public static void Merge(
            this XmlDocument sourceDoc,
            XmlNode sourceRoot,
            XmlNode mergeRoot,
            bool isNetConfigFile)
        {
            #region Check parameters and if needed throw ArgumentNullException
            if (sourceDoc == null)
            {
                throw new ArgumentNullException("sourceDoc");
            }
            if (sourceRoot == null)
            {
                throw new ArgumentNullException("sourceRoot");
            }
            if (mergeRoot == null)
            {
                throw new ArgumentNullException("mergeRoot");
            }
            #endregion

            if (!string.IsNullOrEmpty(mergeRoot.InnerText))
            {
                sourceRoot.InnerText = mergeRoot.InnerText;
            }
            if (!string.IsNullOrEmpty(mergeRoot.Value))
            {
                sourceRoot.Value = mergeRoot.Value;
            }

            #region Copy attributes...
            var mergeRootAttributes = mergeRoot.Attributes;
            if (mergeRootAttributes != null)
            {
                var mergeRootAttributesCount = mergeRootAttributes.Count;

                for (var k = 0; k < mergeRootAttributesCount; k++)
                {
                    var mergeAttribute = mergeRootAttributes[k];
                    var mergeAttributeName = mergeAttribute.LocalName;

                    var sourceAttribute =
                        sourceRoot.Attributes[mergeAttributeName]
                        ?? sourceRoot.Attributes.Append(
                             sourceDoc.CreateAttribute(
                                 mergeAttribute.Prefix,
                                 mergeAttribute.LocalName,
                                 mergeAttribute.NamespaceURI));

                    sourceAttribute.Value = mergeAttribute.Value;
                }
            }
            #endregion

            // loop child nodes...
            var mergeRootNodesCount = mergeRoot.ChildNodes.Count;
            for (var i = 0; i < mergeRootNodesCount; i++)
            {
                XmlNode foundNode = null;
                var mergeNode = mergeRoot.ChildNodes[i];
                var mergeNodeName = mergeNode.LocalName;
                var sourceRootNodeCount = sourceRoot.ChildNodes.Count;

                #region Find node in source...
                for (var j = 0; j < sourceRootNodeCount; j++)
                {
                    var sourceNode = sourceRoot.ChildNodes[j];
                    var sourceNodeName = sourceNode.LocalName;

                    if ((isNetConfigFile &&
                            mergeNodeName == "section" ||
                            mergeNodeName == "sectionGroup"
                        ) ||
                        sourceNodeName != mergeNodeName) continue;

                    foundNode = sourceNode;
                    break;
                }
                #endregion

                #region create a new node...
                if (foundNode == null)
                {
                    foundNode =
                        sourceRoot.AppendChild(
                         sourceDoc.CreateNode(
                            mergeNode.NodeType,
                            mergeNode.Prefix,
                            mergeNode.LocalName,
                            mergeNode.NamespaceURI));
                }
                #endregion

                sourceDoc.Merge(foundNode, mergeNode);
            }
        }
    }
}
A: 

If it works for you...

You should take a look at Linq to XML. It's much better than System.XML: Cleaner code, easier to write code, easier to understand code, less code. Lovely.

Daniel M
Thanks for your input!
Daniel Fisher lennybacon
A: 

You can also take a look on XmlMassUpdate task of the http://msbuildtasks.tigris.org/ it do the same thing...

Mike Chaliy
A: 

I like it... .. I liked it until I tried it. Doesn't quite work.

<?xml version="1.0" encoding="utf-8"?>

<configuration>

  <appSettings>
    <add key="ProxyFolder" value="http://testme:60000/proxy/" />
    <add key="MergeFileName" value="qa.xml" />
  </appSettings>

  <connectionStrings>
    <add name="Ops" connectionString="Data Source=blah blah" providerName="System.Data.SqlClient" />
  </connectionStrings>  

  <system.web>
    <compilation debug="true" />
  </system.web>

</configuration>

Merges against:

  <appSettings>
    <add key="ProxyFolder" value="http://localhost:60000/proxy/" />
  </appSettings>
  <connectionStrings>
    <add name="Ops" connectionString="Data Source=originalfile;" />
  </connectionStrings>

yields:

  <appSettings>
    <add key="MergeFileName" value="qa.xml" />
  </appSettings>
  <connectionStrings>
    <add name="Ops" connectionString="blah blah" />
  </connectionStrings>

works.. kind of. Ideas? email me at: t c webb (at) G mail period com

any solutions ??
alhambraeidos