Thursday, September 6, 2012

Object Serialization and Deserialization in C#

Serialization is the process of converting an object into a stream of bytes in order to persist it to memory, a database, a file or even can be sent over a network. Its main purpose is to save the state of an object in order to be able to recreate it when needed. The reverse process is called Deserialization. When we serialized an object, the object is serialized to a stream, which carries not just the data, but information about the object's type, such as its version, culture, and assembly name.

To object to be serialized, we should apply [Serializable()] attribute. If we don’t want any property to be serialized, we can apply [NonSerialized()] attribute.

Microsoft .NET Framework provides two types of Serialization.
  1. Binary Serialization
    • Binary serialization uses binary encoding to produce compact serialization for uses such as storage or socket-based network streams.
  2. XML Serialization
    • XML serialization serializes the public fields and properties of an object, or the parameters and return values of methods, into an XML stream that conforms to a specific XML Schema definition language (XSD) document. XML serialization results in strongly typed classes with public properties and fields that are converted to XML. You can apply attributes to classes and class members in order to control the way the XmlSerializer serializes or deserializes an instance of the class.
There are few differences between these two types of Serialization.

Binary Serialization
XML Serialization

Basic Serialization

The only requirement in basic serialization is that the object has the [Serializable()] attribute applied. The [NonSerialized()] can be used to keep specific fields from being serialized.

When you use basic serialization, the versioning of objects may create problems, in which case custom serialization may be preferable. Basic serialization is the easiest way to perform serialization, but it does not provide much control over the process.

Custom Serialization

In custom serialization, you can specify exactly which objects will be serialized and how it will be done. The class must be marked [Serializable()] and implement the ISerializable interface. If you want your object to be deserialized in a custom manner as well, you must use a custom constructor.

Now let’s see how this works. I am using Binary Serialization here. I have a class which is “Employee” and I am going to serialize it using Basic Serialization and Custom Serialization.

Basic Serialization using Binary Serialization
    [Serializable()] //this class is serializable
    public class Employee
    {
        public int EmpId;
        public string FirstName;
        public string LastName;

        [NonSerialized()] public string Address; //this field will not be serialized

        public Employee()
        {

        }
    }
What this code snippet does is, I have a class which is “Employee” and it is ready to be serialized. But I don’t want the address field to be serialized.

Now in my method, I am doing the serialization and deserialization.
   class Program
   {
       static string file = "MyObject.abc";

       static void Main(string[] args)
       {
           Assembly assembly = Assembly.GetExecutingAssembly();
           string directoryName = Path.GetDirectoryName(assembly.Location);
           string fileName = Path.Combine(directoryName, file);

           Employee writeEmployee = new Employee();
           writeEmployee.EmpId = 1;
           writeEmployee.FirstName = "Jaliya";
           writeEmployee.LastName = "Udagedara";
           writeEmployee.Address = "Kandy";
           Serialize(writeEmployee, fileName);
           Console.WriteLine("Completed. Press enter key to read.");
           Console.ReadLine();

           Employee readEmployee = Deserialize(fileName);
           Console.WriteLine("Employee Id: {0}", readEmployee.EmpId);
           Console.WriteLine("Employee First Name: {0}", readEmployee.FirstName);
           Console.WriteLine("Employee Last Name: {0}", readEmployee.LastName);
           Console.WriteLine("Employee Address: {0}", readEmployee.Address);
           Console.WriteLine("Completed.");
           Console.ReadLine();
       }

       //method for serialization
       private static void Serialize(Employee employee, string fileName)
       {
           Stream stream = File.Open(fileName, FileMode.Create);
           BinaryFormatter binaryFormatter = new BinaryFormatter();

           Console.WriteLine("Writing Employee Information...");
           binaryFormatter.Serialize(stream, employee);
           stream.Close();
       }

       //method for deserialization
       private static Employee Deserialize(string fileName)
       {
           Employee employee = null;
           Stream stream = File.Open(fileName, FileMode.Open);
           BinaryFormatter binaryFormatter = new BinaryFormatter();

           Console.WriteLine("Reading Employee Information...");
           employee = (Employee)binaryFormatter.Deserialize(stream);
           stream.Close();
           return employee;
       }
   }
In here what happens is, I will be creating a file named “MyObject.abc” and it will store my serialized object. I have two method for serialization and deserialization. Since I am using BinaryFormatter, to serialize my object, I am passing a stream and the object to it’s Serialize() method. And finally to deserialize, I am using Deserialize() method of BinaryFormatter, and for that I am passing again a stream to the Deserialize() method. For better understanding I am using writeEmployee and readEmployee which are two different objects of same class. So in here my output would be,

Untitled
Output

As you can see from above output, everything is serialized except the address field, Because in my program, I have set address field as a non serializable field. Now let’s see how Custom Serialization works.

Custom Serialization using Binary Serialization
    [Serializable()] //this class is serializable
    public class Employee : ISerializable
    {
        public int EmpId;
        public string FirstName;
        public string LastName;
        public string Address;

        public Employee()
        {

        }

        //Custom Deserialization
        public Employee(SerializationInfo info, StreamingContext ctxt)
        {
            EmpId = (int)info.GetValue("EmpId", typeof(int));
            FirstName = (String)info.GetValue("FirstName", typeof(string));
            LastName = (String)info.GetValue("LastName", typeof(string));
        }

        //Custom Serialization
        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("EmpId", EmpId);
            info.AddValue("FirstName", FirstName);
            info.AddValue("LastName", LastName);
        }
    }
In here I have implemented the ISerializable interface and I need to write the GetObjectData method which is for serialization and the Employee constructor which is for deserialization. If you notice that, in here I have not applied [NonSerialized()] attribute to address field even though I don’t want it to be serialized. But instead I have not used address field in serialization and deserialization.

In here SerializationInfo stores all the data needed to serialize or deserialize an object. StreamingContext is a structure describing the source and destination of a given serialized stream. Now when you run this code you will also get the same output as in Basic Serialization.

Hope you got a good understanding about Object Serialization and Deserialization in C#.

Happy Coding.

Regards,
Jaliya