Saturday, March 5, 2016

C# : Using XmlSerializer for creating (storing) objects from (to) XML file


This time, I wanted to share a cool mechanism for handling object presentation between object instance and XML file. This mechanism can be found in System.Xml.Serialization namespace. In short, when using XmlSerializer we can create object instance directly from XML file and store object blueprint information to XML file to be used whenever needed.


Real-life design scheme

 

Assume we have some state-of-the-art analytic calculation engine, into which we feed a lot of different transactions and market scenario data in the first place. When the engine is up and running, we can simply give any specific calculation task object to be processed for this engine. One Task object will simply define a specific set of parameters, such as

  • Calculation task name
  • What kind of transactions will be selected?
  • In what kind of market scenario those selected transactions will be valued?
  • What kind of output values will be calculated for each transaction?

Obviously, the beef is really in handling all required configuration parameters for all calculation tasks in an efficient manner. For this purpose, we need to be able to create calculation task instances from some source. Blueprints for all task objects can be stored into XML files, from which the actual objects are going to be created by XmlSerializer to be processed by the engine.


Bridge over troubled water

 

For the purposes mentioned above, I came up with an idea of generic ObjectXMLHandler<T> class for handling Object/XML transformations for any type of object, not only for Task object which has been used in this example. Behind the scenes, this class is nothing more but a wrapper for XmlSerializer, which can create (store) a list of objects from (to) XML files.

For storing object blueprints into XML file (Serialize), the class client will give a list of object instances, a list of file names to be used and a path name for a specific target folder. Below is a screenshot of one Task object blueprint XML file, created by ObjectXMLHandler.








For creating objects from XML file (Deserialize), the class client will only give a path name for a specific target folder, in which all object blueprints are stored in XML files. When calling this method, the class will return a list of object instances (having type of T) for its client. Below is a screenshot of all three Task object instances which have been created from XML files by ObjectXMLHandler.







Personally, I find XmlSerialization class to be extremely useful for the schemes like the one presented in here. It will completely liberate me from writing my own XML handlers for such cases.

A small dark cloud within this overwhelming joy is the fact, that this presented XmlSerializer class can only handle member data which has been defined to be public. For other cases, I would recommend to dig deeper into implementing IXmlSerializable interface, found in the same namespace.

Thanks for reading my blog. I hope you have got some useful ideas for your own programs.


The Program


Create a new project consisting of three CS files (Program, Task, ObjectXMLHandler). CopyPaste the following program into corresponding CS files.

// Program.cs content
using System;
using System.Collections.Generic;

namespace XMLTester
{
    class Program
    {
        private static string FilePathName = @"C:\temp\Tasks\";
        //        
        static void Main(string[] args)
        {
            try
            {
                // create instance of ObjectXMLHandler for Task type
                ObjectXMLHandler<Task> handler = new ObjectXMLHandler<Task>();
                //
                // A. SERIALIZING (object to XML)
                // create a list of required XML file names
                List<string> fileNames = new List<string>() 
                { 
                    "TaskOne.xml", 
                    "TaskTwo.xml", 
                    "TaskThree.xml" 
                };
                // create a list of calculation task configuration objects
                List<Task> tasks = new List<Task>() 
                { 
                    new Task("ALL_SWAPS_BASE", "ALL", "MARKET_BASE", "PV,DELTA,CVA"),
                    new Task("CITIBANK_SWAPS_STRESSED_CDS", "CITIBANK", "MARKET_CDS_STRESS", "PV,DELTA"), 
                    new Task("DEUTSCHE_SWAPS_STRESSED_FX", "DEUTSCHE", "MARKET_FX_STRESS", "PV,DELTA")
                };
                // write task objects to XML files
                handler.Serialize(tasks, fileNames, FilePathName);
                //
                // B. DE-SERIALIZING (XML to object)
                // create task objects from XML files and print task configuration string
                tasks = handler.Deserialize(FilePathName);
                tasks.ForEach(task => Console.WriteLine(task.ToString()));
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
}
//
//
//
// Task.cs content
using System;

namespace XMLTester
{
    /// <summary>
    /// Class for administrating calculation task parameters. 
    /// </summary>
    public class Task
    {
        public string taskName;
        public string tradeSelection;
        public string marketSelection;
        public string calculationOutput;
        //
        /// <summary>
        /// Default constructor is required by XMLSerializer.
        /// </summary>
        public Task()
        {
            //
        }
        /// <summary>
        /// Parameter constructor for creating one calculation task object.
        /// </summary>
        public Task(string taskName, string tradeSelection, 
            string marketSelection, string calculationOutput)
        {
            this.taskName = taskName;
            this.tradeSelection = tradeSelection;
            this.marketSelection = marketSelection;
            this.calculationOutput = calculationOutput;
        }
        /// <summary>
        /// Get configuration string for calculation task.
        /// </summary>
        public override string ToString()
        {
            return String.Format("{0}.{1}.{2}.{3}", 
                taskName, tradeSelection, marketSelection, calculationOutput);
        }
    }
}
//
//
//
// ObjectXMLHandler.cs content
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;

namespace XMLTester
{
    /// <summary>
    /// Generic template class for handling conversion 
    /// between object and XML presentation.
    /// </summary>
    public class ObjectXMLHandler<T>
    {
        private XmlSerializer serializer = null;
        private Stream stream = null;
        //
        /// <summary>
        /// Parameter constructor for creating an instance. 
        /// </summary>
        public ObjectXMLHandler()
        {
            serializer = new XmlSerializer(typeof(T));
        }
        /// <summary>
        /// Convert a list of objects of type T into XML files. 
        /// </summary>
        public void Serialize(List<T> objects, List<string> fileNames, string folderPathName)
        {
            if (objects.Count != fileNames.Count) 
                throw new Exception("objects.Count != fileNames.Count");
            //
            int counter = 0;
            foreach (T t in objects)
            {
                stream = File.Create(Path.Combine(folderPathName, String.Concat(fileNames[counter])));
                serializer.Serialize(stream, t);
                stream.Close();
                counter++;
            }
        }
        /// <summary>
        /// Convert XML files in specific folder into a list of objects of type T.
        /// </summary>
        public List<T> Deserialize(string folderPathName)
        {
            List<T> objects = new List<T>();
            foreach (string t in Directory.GetFiles(folderPathName))
            {
                stream = File.OpenRead(t);
                objects.Add((T)serializer.Deserialize(stream));
                stream.Close();
            }
            return objects;
        }
    }
}

No comments:

Post a Comment