Object Serialization

Object serialization is the process of rendering an object into a state that can be stored persistently. The Java platform has included serialization support since version 1.1, and while there have been additional enhancements, the model has proved sufficiently flexible to meet the needs of most developers.

In .NET, the way in which serialized data is stored is controlled by formatters, which are classes that implement the System.Runtime.Serialization.IFormatter interface. Two implementations are available in the .NET Framework:

  • System.Runtime.Serialization.Formatters.Binary.BinaryFormatter

  • System.Runtime.Serialization.Formatters.Soap.SoapFormatter

The BinaryFormatter produces a compact and efficient binary representation of the state of an object. The SoapFormatter produces an XML SOAP document that can be parsed like any other XML document. Both formatters accept an instance of System.IO.Stream to read from or write to and so can be used to serialize to a range of backing stores, including files and network connections.

Simple Serialization

Java classes are not serializable by default and are required to implement the java.io.Serializable interface before they can be persisted. .NET classes require annotation with the Serializable attribute before they can be processed by the formatter.

The following class demonstrates the use of this attribute:

[Serializable]
class SimpleClass {
    private int o_int;
    private string o_string;

    public int SimpleInt {
        get {
            return o_int;
        }
        set {
            o_int = value;
        }
    }

    public String SimpleString {
        get {
            return o_string;
        }
        set {
            o_string = value;
        }
    }
}

This class maintains two private members, which are accessible via public properties. Because the class is annotated, it can be serialized and restored as shown next. We use these statements to serialize all of the example classes in this section:

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

class Test {
    public static void Main() {
        SimpleClass x_simple = new SimpleClass();
        x_simple.SimpleInt = 20172;
        x_simple.SimpleString = "C# for Java Developers";
        FileStream x_stream = new FileStream("serial.bin",
             FileMode.OpenOrCreate, FileAccess.Write);

        IFormatter x_formatter = new BinaryFormatter();
        x_formatter.Serialize(x_stream, x_simple);
        x_stream.Close();

        FileStream x_in_stream = new FileStream("serial.bin",
            FileMode.Open, FileAccess.Read);
        x_simple = (SimpleClass)x_formatter.Deserialize(x_in_stream);
        x_in_stream.Close();

        Console.WriteLine("String value: " + x_simple.SimpleString);
        Console.WriteLine("Int value: " + x_simple.SimpleInt);
    }
}

We start by creating an instance of the serializable class, named SimpleClass, and set the values for the private variables. We then create a FileStream that will be used to store the serialized data, followed by a BinaryFormatter. The formatter is created with this statement:

IFormatter x_formatter = new BinaryFormatter();

Alternatively, if we wanted a SoapFormatter, the syntax would be

IFormatter x_formatter = new SoapFormatter();

Finally we call the IFormatter.Serialize method, passing in the stream and the object to persist.

Restoring the object is just as easy. We create a new FileStream that will read from the file that we created and then call the IFormatter.Deserialize method passing in the stream. In common with the Java serialization support, the Deserialize method returns an object, which must be cast to SimpleClass before we can print the values of the int and string members.

This example demonstrates the basic serialization of an object. The following sections illustrate how the programmer can exert more control over the serialization process and select which elements should be persisted.

Selective Serialization

In the preceding section, both the int and string fields were serialized. .NET provides support for selecting fields to be omitted from the persistent data. Fields marked with the NonSerialized attribute will not be persisted.

The following code fragment demonstrates the use of this attribute to indicate that the int field should not be serialized for the SimpleClass type:

[Serializable]
class SimpleClass {

[NonSerialized] private int o_int;
private string o_string;

Using the formatter as shown results in the following output:

String value: C# for Java Developers
Int value: 0

When the instance is restored from the file, there is no persisted value for o_int, so the default value (0) is displayed.

Custom Serialization

By implementing the System.Runtime.Serialization.ISerializable interface, a developer can manually control which fields a class serializes. This is the functional equivalent of java.io.Externalizable, allowing the programmer to take responsibility for the serialization process.

The ISerializable interface contains a single method, GetObjectData, which is called when the object is written to a formatter. Classes that implement the interface must also provide a constructor that accepts the same arguments as the GetObjectData method. Note that types that implement ISerializable must still be annotated with the Serializable attribute. Here is SimpleClass, modified to implement the interface and the constructor:

[Serializable]
class SimpleClass : ISerializable {
    private int o_int;
    private string o_string;

    public SimpleClass() {
    }

    protected SimpleClass(SerializationInfo p_info,
        StreamingContext p_context) {

        o_int = p_info.GetInt32("int");
        o_string = p_info.GetString("string");
    }

    public int SimpleInt {
        get {
            return o_int;
        }
        set {
            o_int = value;
        }
    }

    public String SimpleString {
        get {
            return o_string;
        }
        set {
            o_string = value;
        }
    }

    public virtual void GetObjectData(SerializationInfo p_info,
        StreamingContext p_context) {

        p_info.AddValue("int", o_int);
        p_info.AddValue("string", o_string);
    }
}

The changes appear in boldface, the first being a default constructor, required because we have added the protected form for the ISerializable support. The GetObjectData method takes two arguments, a SerializationInfo and a StreamingContext.

The StreamingContext represents the scope of the serialized data. When either a BinaryFormatter or a SoapFormatter is created, it’s possible to specify a value from the StreamingContext enumeration, which will be made available as the argument to GetObjectData. The default context state is All, indicating that the data can be transmitted to any other context or application domain. Setting different values for this scope allows the programmer to selectively serialize data based on where it will be used—for example, omitting fields when the data will cross to another process but ensuring that all fields are serialized when the object is persisted to a local file.

The SerializationInfo type is used to serialize and restore fields. In the example GetObjectData method, we use the AddValue method to store an integer and a string. Overloaded forms of this method accept all primitive data types and all objects, which will in turn be serialized. Each value added with AddValue is stored as a key/value pair, and the programmer is able to specify any string as the key. These keys are then used in the special constructor to retrieve the values and restore the object state.

Important

Classes that are derived from types that implement ISerializable must implement the GetObjectData and the special constructor and must ensure that the base versions of these members are invoked.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset