3/30/14

XDocument Merge XML Documents

In some instances, we may have the need to extend the information in a XML payload by merging multiple documents into one. When using the System.Xml.Linq.XDocument class, there is no method to allow us to do a merge. On this article, we extend the XDocument class to provide the Merge capabilities.

XML Documents:

We first start by showing two XML documents that can be combined into one. The base requirement with this approach is that both documents are similar with the exception of one Element that contains some unique information that is not contained on the other XML.

XML with Annual Data
XML with YTD Data
Merged XML

<Finance>
  <Company>
    <Name>OG-BIT</Name>
  </Company>
  <Annual>
    <col1>100</col1>
    <col2 />
  </Annual>
</Finance>


<Finance>
  <Company>
    <Name>OG-BIT</Name>
  </Company>
  <YTD>
    <col1>100</col1>
    <col2>200</col2>
  </YTD>
</Finance>


<Finance>
  <Company>
    <Name>OG-BIT</Name>
  </Company>
  <YTD>
    <col1>100</col1>
    <col2>200</col2>
  </YTD>
  <Annual>
    <col1>100</col1>
    <col2 />
  </Annual>
</Finance>


The company and annual information needs to be maintained.

From this XML, we need to extract the YTD information

The outcome should be a document with both reports and with single company information.

Extension Methods:

An approach to provide this feature is to create extension methods that implement the merge. This is done by adding the following static class:

    /// <summary>
    /// extension methods to enable the merge of xml documents
    /// </summary>
    internal static class XDocumentExtension
    {
        /// <summary>
        /// merge an element from one document to the root of another
        /// </summary>
        /// <param name="refDoc"></param>
        /// <param name="doc"></param>
        /// <param name="elementName"></param>
        internal static void Merge(this XDocument refDoc, XDocument doc, string elementName)
        {
            if (doc != null && !String.IsNullOrWhiteSpace(elementName))
            {

                //find the element by name
var segment = doc.Descendants().Where(elm => elm.Name.LocalName= elementName).Select(elm => elm).FirstOrDefault();
                //if found add to the original document at the root
                if (segment != null)
                    refDoc.Root.Add(segment);           
            }
        }

        /// <summary>
        /// merge an xml string into a XDocument reference
        /// </summary>
        /// <param name="refDoc"></param>
        /// <param name="xml"></param>
        /// <param name="elementName"></param>
        internal static void Merge(this XDocument refDoc, string xml, string elementName)
        {
            if (!String.IsNullOrWhiteSpace(xml) &&                              !String.IsNullOrWhiteSpace(elementName))
            {
                XDocument doc = XDocument.Parse(xml);
                refDoc.Merge(doc, elementName);
            }
        }

        /// <summary>
        /// returns the xml string with header declaration
        /// </summary>
        /// <param name="doc"></param>
        /// <returns></returns>
        internal static string ToStringWithDeclaration(this XDocument doc)
        {
            return String.Format("{0}{1}", doc.Declaration, doc.ToString());
        }       
    }

This is the description for this class:

Method
Description
Merge (XDocument, string)
Merges two XDocument references by finding an element with the LocalName equal to the parameter value.
Merge (string, string)
Loads the XML string into an XDocument reference and calls the Merge method.
ToStringWithDeclaration
Gets the merged XML string with the Declaration header when found (i.e. <?xml>)

Example:

The following example uses the extension methods to merge the documents and returns the merged XML string:

public static void main()
{
    string xml1 = @"<Finance><Company><Name>OG-BIT</Name></Company><YTD><col1>100</col1><col2>200</col2></YTD></Finance>";
    string xml2 = @"<Finance><Company><Name>OG-BIT</Name></Company><Annual><col1>100</col1><col2/></Annual></Finance>";

   XDocument doc = XDocument.Parse(xml1);
   doc.Merge(xml2, "Annual");
   string xml = doc.ToStringWithDeclaration();
}

This simple example illustrates an approach of how this can be done when using the XDocument class. There are other ways of achieving the same with other classes.


Thanks for reading.

0 comments :

Post a Comment