Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / XML

Custom Serialization in .NET - Part 3

1.67/5 (3 votes)
26 Mar 2008CPOL4 min read 1   337  
This article will give you an overview of how you can implement your own serialization engine.

Introduction

In my previous article, I covered the various ways of serialization techniques available in .NET. In this article, I will give you an overview of how we can implement our own serialization engine.

Background

This article is a continuation of the earlier one – Custom Serialization – Part 2.

Serialization engine

When .NET offers serialization, by default, why do we need to develop a custom solution? Well, there are some features that we can build in if we do so, and they are:

  • Ability to dynamically control the properties to be or not to be serialized. This would help us to serialize only those properties that a user is allowed to view based on authorization roles & rules.
  • Ability to control the format of the serialized data dynamically. For example, serialize an object into an XML element or an XML attribute, dynamically.
  • Interoperability within an enterprise is much easier with other platforms since the format is controlled by us and the same format can be shared with other platforms.

Now that we have identified the whys and the benefits of a custom serialization engine, we will look into the steps of building the same. The serialization engine would serialize an object into an XML string and deserialize the XML string back into an object. The engine would be capable of performing this for any object. The engine would do so by getting the list of properties and converting them into an XML string based on a configuration file. For convenience, I would term it as a mapping file.

The mapping file would help the engine to convert an object into XML and vice versa. The mapping file would contain the following information to achieve the same:

  • Object information like - Class name, Type, Assembly, Schema file
  • List of properties
  • Property data type
  • XPath of each property in the XML document

The mapping file for the Customer object is given below:

CustomSerialization-Part3-MappingFile.JPG

The schema file for the Customer object is given below:

CustomSerialization-Part3-SchemaFile.JPG

The schema file will define the format of the customer object when it is serialized into XML. In this example, the Customer object properties are defined as XML elements. We can also correlate the mapping file with the schema file for every property XPath.

Serialization logic

Now, we have done the ground work, and we can start implementing our custom serialization engine. The serialization logic would be as follows:

  1. Accept the object and the mapping file name as parameters
  2. Load the mapping XML file
  3. Based on the schemaFile attribute value in the mapping file, generate an empty XML from the XSD file
  4. Loop through all the properties defined as per “serializer/object/properties”
    • For every node, get the name and get the value from the instance’s property getter
    • Set the value in the xml based on the XPath
    • Perform steps 4 a, b for every property in the mapping file
  5. The XML document now contains the XML representation of the instance passed

The following code snippet shows the serialization logic:

C#
public string Serialize(object instance, string mappingFile)
{
    Mapper mapper = Mapper.GetMappingFile(mappingFile);
    string xml = Serializer.GenerateXml(mapper.DataObject.SchemaFile);

    XmlDocument document = new XmlDocument();
    document.LoadXml(xml);

    Type type = instance.GetType();
    foreach (DataObjectProperty property in mapper.DataObject.Properties)
    {
        object oValue = type.InvokeMember(property.Name, BindingFlags.GetProperty | 
                        BindingFlags.Instance | BindingFlags.Public, null, instance, null);
        string value = oValue.ToString();

        XmlNode node = document.SelectSingleNode(property.Xpath);
        node.InnerXml = value;
    }

    return document.ChildNodes[1].OuterXml;
}

Mapper is a strongly typed class that represents the mapping file. The mapping XML file is formed as a strongly typed instance and loaded into a Hashtable. This avoids re-loading of the XML file for every serialization logic, and improves performance.

Generating XML from XSD

In order to generate an empty XML from a given XSD file, I have used this MSDN article's code. This takes in an XSD file and returns an empty XML. See the Microsoft.Xml.XMLGen project that is part of the code download.

C#
private static string GenerateXml(string schemaFile)
{
    StringBuilder builder = new StringBuilder();
    XmlWriter writer = XmlWriter.Create(builder);

    XmlSampleGenerator xmlGenerator = 
       new XmlSampleGenerator(schemaFile, null);

    xmlGenerator.WriteXml(writer);
    string xml = builder.ToString();
    return xml;
}

Deserialization logic

The de-serialization logic would be exactly opposite to the serialization logic:

  1. Accept the XML string and the mapping file name as parameters
  2. Load the mapping XML file
  3. Load the XML string
  4. Based on the schemaFile attribute value in the mapping file, load the assembly and create the instance
  5. Loop through all the properties defined as per “serializer/object/properties”
    • For every node, get the name, and set the value to the instance’s property setter
    • Perform step 5 a for every property in the mapping file
  6. The XML document now contains the XML representation of the instance passed

The following code snippet shows the de-serialization logic:

C#
public object Deserialize(string xmlData, string mappingFile)
{
    XmlDocument document = new XmlDocument();
    document.LoadXml(xmlData);
    
    Mapper mapper = Mapper.GetMappingFile(mappingFile);


    Assembly assembly = Assembly.Load(mapper.DataObject.Assembly);
    object instance = assembly.CreateInstance(mapper.DataObject.Type);
    Type type = instance.GetType();

    foreach (DataObjectProperty property in mapper.DataObject.Properties)
    {
        XmlNode node = document.SelectSingleNode(property.Xpath);

        if ( property.NodeType == 
        string value = node.InnerXml;

        Type memberType = type.GetProperty(property.Name, 
                          BindingFlags.Instance | BindingFlags.Public).PropertyType;

        object oFinalValue = Convert.ChangeType(value, memberType);
        type.InvokeMember(property.Name, BindingFlags.SetProperty | BindingFlags.Instance | 
                          BindingFlags.Public, null, instance, new object[] { oFinalValue });
    }

    return instance;
}

About the sample application

The sample application contains the customer form that accepts data to form the customer instance. The Serialize button serializes the customer object into an XML string and displays in the result text box. The Deserialize button forms the object from the XML in the result text box and displays in the controls.

To invoke the serialization process:

C#
CustomSerializer.Serializer serializer = new CustomSerializer.Serializer();
textBoxResult.Text = serializer.Serialize(customer, mappingFile);

To invoke the de-serialization process:

C#
CustomSerializer.Serializer serializer = new CustomSerializer.Serializer();
Customer customer = (Customer)serializer.Deserialize(textBoxResult.Text, mappingFile);

Application snapshot:

Image 3

Happy coding, Happy reading!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)