by September 12, 2002 0 comments



Object serialization has been present from almost the times objects came into being. However, the power of serialization was realized only after its introduction in the MFC (Microsoft Foundation Classes). Today, the scenario is become such that an object almost cannot survive without serialization support. 

Serialization is all about maintaining state of an entity, and in the case of objects, it is all about maintaining the object state. This means that a class has its object instantiated, its properties are set and hence, its state is defined and set. Next, the object is serialized such that its state is stored to some persistent storage device, like a file on the hard disk. Finally, the object is destroyed, and all related memory is freed.

Note that if the object is recreated, it will be recreated from the scratch. In order for the object it holds the same state before it was destroyed, we have two options:

l Either we work upon the object in the same way as we did earlier to get to its original state before it was destroyed 
l Or, we could deserialize the object, which refers to loading of the previously saved object state 

Once deserialized, the object holds the same state as it had before it got destroyed, without undergoing the steps to reach the state in question. Hence, we see that serialization and deserialization help us a good deal to maintain and reload the object state. 

This concept is similar to hibernation of the Windows OS. When hibernated, Windows saves the current memory state, the applications that are running, the documents that are being worked upon, on the hard disk, and shuts down. When restarted, it resumes the previous session by reloading the saved state by loading the contents of the memory, the running applications, etc that were active at the time of hibernation. Essentially, the hibernation of Windows involves serialization and deserialization, though at the OS level.

Now you may wonder why, and when would we need to serialize/deserialize objects? Well, there are plenty of scenarios, like:

l You want to transfer your custom class object over a .Net Web service. A web service can only transfer objects that can
be serialized/deserialized. Hence, you will have to implement serialization support in your class implementation. 
l You may want to save on memory. If you have a large number of serializable objects loaded in memory, but not all of them
are required at any given point of time, you can always serialize them and free the memory being used. When the need is
there, you can always deserialize them, and get them up in action. 

With the basics done, let us understand the focus of this article. In the first part, we shall see how to use serialization support available in .Net to serialize and deserialize objects that support serialization. We shall work with both binary and SOAP serializations. And once we understand how to serialize objects, we shall proceed to understand how to implement custom serialization support in our classes.

Serializing objects
For a class to be able to get serialized, it must announce to its client that it can be serialized. This is done by using the [Serializable] attribute as shown in the code below:

[Serializable]
public class MyClass
{
// this is a serializable member
public string _strName=null;

// this is an un-serializable member
[NonSerialized]
public int _iAge=0;
}; 

The class definition is prefixed with the [Serializable] attribute. This ensures that an object of this class can be serialized to a persistent storage. In fact, the .Net serialization support is so sophisticated that, prior to serialization process, an object graph is built at runtime that provides a link between all the objects to which the object being serialized holds references, and all such objects get serialized as well. The point to be kept in mind is that the object graphs give a relationship that define how the objects refer to each other, and may not conform to the standard OO relationships at all. 

Now, even within a serializable class, you may not want to serialize certain members. In order to prevent specific members from getting serialized, all you have to do is specify the [NonSerialized] attribute before them, as shown in the example above. Hence, when the above class’s object gets serialized, the _strName member is serialized, while _iAge isn’t. Thus, simply by looking at a class definition you can identify whether it’s a candidate for serialization or not.

Once a class is configured for serialization, the next issue relates to deciding the format in which it shall be serialized. By default, the .Net framework ships with two formatters: the Binary formatter and the SOAP formatter. While the former is implemented in the mscorlib.dll assembly, the latter is implemented in the System.Runtime.Serialization.Formatters.Soap.dll assembly. 

The formatters are responsible for serializing the object state into the format they support, and are available under System.Runtime.Serialization.Formatters namespace. In case you require more than what the default formatters have to offer, the System.Runtime.Serialization namespace contains enough support for you to implement your own formatter. Irrespective of the formatter you use, the point to be noted is that it is the formatter that interacts with the stream to persist, and unpersist, the object state to the entity identified by the stream (memory, network, file, etc). The serialization, hence, can be visualized 
as shown in graphics.

The deserialization process takes the exactly reverse path: going from the storage via the formatter to the creation of a new object instance. With enough serialization/deserialization background, now lets take the plunge and serialize/deserialize our above exemplified class. Below is the sample class implementation that serializes and deserializes an object of MyClass, as defined above:

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

public class Client
{
public static void Main()
{
MyClass mc = new MyClass(); 
mc._strName=”Gaurav”;
mc._iAge=23;
// serialize it to a file..
FileStream fs = File.Create(“mc.dat”);
// create the binary formatter…
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(fs,mc);
fs.Close();
Console.WriteLine(“Serialized the name and age to {0} and {1}
respectively”,mc._strName,mc._iAge);
// set the age and name to different values..
mc._strName=”Kumar”;
mc._iAge=20; 
Console.WriteLine(“Changed the name and age to {0} and {1} respectively”,mc._strName,mc._iAge);
// now read the serialized object..
fs = File.OpenRead(“mc.dat”);
mc=(MyClass)bf.Deserialize(fs);
fs.Close();
// show the current contents..
Console.WriteLine(“Values from the serialized object have set name and age to {0} and {1}
respectively”,mc._strName,mc._iAge);
}
}

The above code uses the BinaryFormatter class, defined in the System.Runtime.Serialization.Formatters.Binary namespace, to serialize the object state to binary format, and deserialize it as well. The code starts off by instantiating the class object, and setting its public members.

Next, a file, mc.dat, is created, followed by an instantiation of the BinaryFormatter object, whose Serialize method is invoked to serialize the object state to the file created. The first argument to the method is file stream object that represents the file to which the object state will be serialized, while the second argument is object that needs to be serialized.

Next, the current values of the public members are displayed. This is followed by resetting the values, and displaying them to the user, followed by deserializing the object by invoking the Deserialize method of the BinaryFormatter object. Note the during
deserialization, we must box the returned value to the correct class since the Deserialize method returns an Object.
Finally, the values of the public members are displayed again, which are now the same ones that were
deserialized. However, since _strName was serializable, its serialized value will be displayed, while
_iAge has its default value set as it was tagged as [NonSerialized]. The following is the screenshot of the output, which confirms the expected behavior:

Though the example client code above uses the BinaryFormatter to serialize the MyClass object, we could easily modify it to use the SoapFormatter to serialize the object as an XML file. The steps to that are as follows:

Modify the namespace from System.Runtime.Serialization.Formatters.Binary to
System.Runtime.Serialization.Formatters.Soap 

Update all references of BinaryFormatter class to SoapFormatter class  

Lastly, add a reference to the System.Runtime. Serialization.Formatters.Soap.dll 

By now, we have gained a good idea of what serialization/deserialization is all about, how to make a class
serializable, with portions of it unserializable, and finally, how to serialize and deserialize an object of a serializable class using the default Binary and SOAP formatters. 

Though, both these formatters provide a good implementation of serialization and deserialization processes, what if you wish to customize the entire process, say, by encrypting the object state? In such scenarios, custom serialization is implemented for a class, and this is the second part of this article. So, lets get started. 

When the serialization/deserialization process needs to be customized, the class requiring the custom implementation must implement the ISerializable interface, which we will discuss next month.

Kumar Gaurav Khanna

No Comments so far

Jump into a conversation

No Comments Yet!

You can be the one to start a conversation.

<