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]);
}
}
}