Tuesday, October 14, 2008

Binary & XML Serialization

Serialization is a method for implementing object persistence. It is the process of saving an object onto a storage medium or to transmit it across a network connection link in binary form. Process of serializing an object is also called deflating or marshalling an object. Similarly, deserializing data is referred to as inflating or unmarshalling.

Binary and XML serialization is very popular in the enterprise environment. The BinaryFormatter class is used for binary serialization (Namespace:  System.Runtime.Serialization.Formatters.Binary). Data can be written to a file and it can be retrieved easily. Code snippet for serialization & deserialization is given below.

using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

private void Serialize(object data, string fileName)
{
    try
    {
        // Create the stream for writing data
        using (FileStream stream = new FileStream(fileName, FileMode.Create))
        {
            BinaryFormatter formatter = new BinaryFormatter();

            // Serialize the object
            formatter.Serialize(stream, data);

            // Cleanup
            stream.Close();
        }
    }
    catch
    {
        throw;
    }
}

The function accepts an object and it will be serialized to a file. One point is; to serialize a class, it should be marked with the Serializable attribute, as shown:

[Serializable()]
public class Employee
{
    // Code...
}

To deserialize the data, below given function can be used.

private object Deserialize(string fileName)
{
    object data = null;

    try
    {
        // Open the stream for reading serialized data
        using (FileStream stream = new FileStream(fileName,
                                                FileMode.Open,
                                                FileAccess.Read))
        {
            BinaryFormatter formatter = new BinaryFormatter();

            // Deserialize object
            data = formatter.Deserialize(stream);
        }
    }
    catch
    {
        throw;
    }

    return data;
}

The returned object can be converted to the original data (type-case operation).

In case of XML serialization, objects are serialized and deserialized into and from XML documents. There are several advantages for XML serialization - storing user preferences, maintaining security information across pages and applications, XML manipulation without using DOM, passing objects between application or domains,passing objects through firewall as XML string etc.

For XML serialization, we can create classes annotated with attributes or by using the XML Schema Definition Tool (Xsd.exe) to generate classes based on an existing XSD document.

Method 1 - Classes annotated with attributes

In this method, class members are annotated with certain attributes for serialization. Attributes like XmlRootAttribute, XmlAttributeAttribute, XmlElementAttribute etc. are applied at required levels. These attributes are available in the System.Xml.Serialization namespace. An XmlSerializer object is used for serialization & deserialization. Check the below link for an example:

http://giri.tp.googlepages.com/XMLSerializationDemo.zip

Method 2 - Classes generated using Xsd.exe (from existing XSD documents)

The xsd command (available from the Visual Studio Command Prompt) can be used for the generation of classes from XSD files. For example, save the below given XSD with name books.xsd.

<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
    elementFormDefault="qualified">
<xsd:element name="bookstore" type="bookstoreType"/>
<xsd:complexType name="bookstoreType">
  <xsd:sequence maxOccurs="unbounded">
   <xsd:element name="book"  type="bookType"/>
  </xsd:sequence>
</xsd:complexType>
<xsd:complexType name="bookType">
  <xsd:sequence>
   <xsd:element name="title" type="xsd:string"/>
   <xsd:element name="author" type="authorName"/>
   <xsd:element name="price"  type="xsd:decimal"/>
  </xsd:sequence>
  <xsd:attribute name="genre" type="xsd:string"/>
</xsd:complexType>
<xsd:complexType name="authorName">
  <xsd:sequence>
   <xsd:element name="first-name"  type="xsd:string"/>
   <xsd:element name="last-name" type="xsd:string"/>
  </xsd:sequence>
</xsd:complexType>
</xsd:schema>

Now, from the Visual Studio command line, execute the following command to generate the C# class for this XSD file.

xsd books.xsd /c /l:cs

This will create a file books.cs in the current directory and it can be added directly to the C# project. The generated file will have all the necessary attributes for XML serialization.

Code snippets for converting an object to XML string and to reconstruct the original object from the XML is given below. This method is very useful in situations like passing objects across a network, through Firewall.

First, we need to define the class which is to be serialized/deserialized. Example:

public class Employee
{
    private string empID;

    public string EmpID
    {
        get { return empID; }
        get { empID = value; }
    }

    private string empName;

    public string EmpName
    {
        get { return empName; }
        set { empName = value; }
    }
}

Now, we can use the below functions for serializing/deserializing an Employee object.

using System.IO;
using System.Xml;
using System.Xml.Serialization;

public string Serialize(object data)
{
    string xml = string.Empty;

    try
    {
        using (MemoryStream stream = new MemoryStream())
        {
            XmlSerializer serializer = new XmlSerializer(typeof(Employee));

            UTF8Encoding encoding = new UTF8Encoding(false);

            // Text writer for writing XML data
            using (XmlTextWriter writer = new XmlTextWriter(stream, encoding))
            {
                // Serialize data
                serializer.Serialize(writer, data);

                using (MemoryStream tmpStream = (MemoryStream)writer.BaseStream)
                { 
                    // Get XML string from memory
                    xml = encoding.GetString(tmpStream.ToArray());
                }
            }
        }
    }
    catch
    {
        throw;
    }

    return xml;
}

public object Deserialize(string xml)
{
    object data = null;

    try
    {
        UTF8Encoding encoding = new UTF8Encoding(false);

        // Initialize memory stream using byte array
        using (MemoryStream stream = new MemoryStream(encoding.GetBytes(xml)))
        {
            // Text writer for writing XML data
            using (XmlTextWriter writer = new XmlTextWriter(stream, encoding))
            {
                XmlSerializer serializer = new XmlSerializer(typeof(Employee));

                // Deserialize object
                data = serializer.Deserialize(stream);
            }
        }
    }
    catch
    {
        throw;
    }

    return data;
}

No comments: