Utility to Patch XML

24. October 2006 10:56 by Mrojas in General  //  Tags:   //   Comments (0)
Recently I was in the need to PATCH an xml. I had a program that generated an XML that was input to other program.
My problem was that there were some particular changes I needed to do to my XML but they could need to be redone If i regenerated my XML, so how could I automate that. I could not find an easy tool to do that so I built one and here it is:
It uses the XML facilities in .NET and it is a quick and dirty implementation. It allows you to Comment tags, to remove them or to add tags and also attributes

Here is a sample input file


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

<PatchesInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">>

 <!-- Sample Patch -->

  <Patches>

    <Patch forAttribute="false">

      <Type>Apply</Type>

      <Content>

       <![CDATA[  <ADDEDTAG/>]]>

      </Content>

      <XPathLocation>//MyData[@name='Entry1']/MyAttribytes/Attribute[@name='Att58']/Annotations</XPathLocation>

   </Patch>

   <!-- Commenting -->

    <Patch forAttribute="false">

      <Type>Comment</Type>

      <Content>

        <![CDATA[** This part with be put on top of the comment entry ** ]]>

      </Content>

      <XPathLocation>//MyData[@name='Entry1']/MyAttribytes/Attribute[@name='Att59']/Annotations</XPathLocation>

    </Patch>

 
using System;

using System.IO;

using System.Collections.Generic;

using System.Text;

using System.Xml;

using System.Xml.Serialization;

using System.Diagnostics;

 

namespace XMLPatcher

{

    // Set this 'Customer' class as the root node

    // of any XML file its serialized to.

    [XmlRootAttribute("PatchesInfo", Namespace = "", IsNullable = false)]

    public class Patches

    {

 

        /// <summary>

        /// Default constructor for this class

        /// (required for serialization).

        /// </summary>       

        public Patches()

        {

        }

 

        [XmlArray("Patches"),XmlArrayItem("Patch",typeof(Patch))]

        public System.Collections.ArrayList patches = new System.Collections.ArrayList();

       

    }

 

    public class Patch

    {

 

        // Set this 'bool' field

        // to be an attribute of the root node.

        [XmlAttributeAttribute]       

        public bool forAttribute = false;

 

        // By NOT specifing any custom

        // Metadata Attributes, fields will be created

        // as an element by default.

        [XmlElement]

        public string Type;

        [XmlElement]

        public string Content;

        [XmlElement]

        public string XPathLocation;

 

    }

 

    static class XmlPatcher

    {

        public static void Patch(string patchFilename,string inputFilename,string outputFilename)

        {

            Patches p = new Patches();

            XmlSerializer serializer = new XmlSerializer(typeof(Patches));

            TextReader reader = new StreamReader(patchFilename);

            p = (Patches)serializer.Deserialize(reader);

            reader.Close();

 

            XmlDocument doc = new XmlDocument();

            doc.Load(inputFilename);

            foreach (Patch patch in p.patches)

            {

                if (patch.forAttribute)

                {

                    if (patch.Type.Equals("Change"))

                    {

                        XmlAttribute node = doc.SelectSingleNode(patch.XPathLocation) as XmlAttribute;

                        node.Value = patch.Content;

                    }

                    else

                        if (patch.Type.Equals("Apply"))

                        {

                            XmlAttribute node = doc.SelectSingleNode(patch.XPathLocation) as XmlAttribute;

                            XmlDocument temp = new XmlDocument();

                            temp.LoadXml(patch.Content);

                            node.AppendChild(doc.ImportNode(temp.ChildNodes[0], true));

                            //doc.RemoveChild(node);

                        }

                        else

                            if (patch.Type.Equals("ApplyAllElements"))

                            {

                                XmlAttribute node = doc.SelectSingleNode(patch.XPathLocation) as XmlAttribute;

                                doc.RemoveChild(node);

                            }

                            else

                                Debug.Fail("Invalid path type for an attribute");

                }

                else

                {

                    //For elements

                    XmlNodeList elements = doc.SelectNodes(patch.XPathLocation);

                    foreach (XmlElement element in elements)

                    {

                        if (patch.Type.Equals("Apply"))

                        {

                            XmlDocument temp = new XmlDocument();

                            temp.LoadXml(patch.Content);

                            element.AppendChild(doc.ImportNode(temp.ChildNodes[0], true));

                        }

                        else if (patch.Type.Equals("Remove"))

                        {

                            element.ParentNode.RemoveChild(element);

                        }

                        else if (patch.Type.Equals("Comment"))

                        {

                            XmlComment comment = doc.CreateComment(patch.Content + "\r\n" + element.OuterXml + "\r\n ********");

                            element.ParentNode.ReplaceChild(comment, element);

                        }

 

                    }

                }

            }

            doc.Save(outputFilename);

            Console.WriteLine("File " + inputFilename + " has been patched. Results in " + outputFilename);

        }

 

    }

 

    class Program

    {

       

        /// <summary>

        /// Reads a Patches document. This document has the following form:

        /// <Patches>

        ///   <Patch xpath="...">

        ///     new xml

        ///   </Patch>

        /// </Patches>

        /// </summary>

        /// <param name="args"></param>

        public static void Main(string[] args)

        {

 

            XmlPatcher.Patch(args[0], args[1], args[2]);

            Console.WriteLine("File " + args[1] + " has been patched. Results in " + args[2]);

        }

    }

}