4.5. Object Serialization

 < Day Day Up > 

In .NET, serialization refers to the process of converting an object or collection of objects into a format suitable for streaming across a network a Web Service, for example or storing in memory, a file, or a database. Deserialization is the reverse process that takes the serialized stream and converts it back into its original object(s).

.NET support three primary types of serialization:

  • Binary. Uses the BinaryFormatter class to serialize a type into a binary stream.

  • SOAP. Uses the SoapFormatter class to serialize a type into XML formatted according to SOAP (Simple Object Access Protocol) standards.

  • XML. Uses the XmlSerializer class to serialize a type into basic XML (described in Chapter 10, "Working with XML in .NET"). Web Services uses this type of serialization.

Serialization is used primarily for two tasks: to implement Web Services and to store (persist) collections of objects to a medium from which they can be later resurrected. Web Services and their use of XML serialization are discussed in Chapter 18, "XML Web Services." This section focuses on how to use binary serialization to store and retrieve objects. The examples use File I/O (see Chapter 5) methods to read and write to a file, which should be easily understood within the context of their usage.

Binary Serialization

The BinaryFormatter object that performs binary serialization is found in the System.Runtime.Serialization.Formatters.Binary namespace. It performs serialization and deserialization using the Serialize and Deserialize methods, respectively, which it inherits from the IFormatter interface.

Listing 4-9 provides an example of binary serialization using simple class members. A hash table is created and populated with two Chair objects. Next, a FileStream object is instantiated that points to a file on the local drive where the serialized output is stored. A BinaryFormatter is then created, and its Serialize method is used to serialize the hash table's contents to a file. To confirm the process, the hash table is cleared and the BinaryFormatter object is used to deserialize the contents of the file into the hash table. Finally, one of the members from a restored object in the hash table is printed verifying that the original contents have been restored.

Listing 4-9. Serialization of a Hashtable
 using System; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; using System.IO; // Store Chair objects in a Hashtable Hashtable ht = new Hashtable(); // Chair and Upholstery must have [Serializable] attribute Chair ch = new Chair(100.00D, "Broyhill", "10-09"); ch.myUpholstery = new Upholstery("Cotton"); ht.Add("10-09", ch); // Add second item to table ch = new Chair(200.00D, "Lane", "11-19"); ch.myUpholstery = new Upholstery("Silk"); ht.Add("11-19", ch); // (1) Serialize // Create a new file; if file exits it is overwritten FileStream fs= new  FileStream("c:\\chairs.dat",                                FileMode.Create); BinaryFormatter bf= new BinaryFormatter(); bf.Serialize(fs,ht); fs.Close(); // (2) Deserialize binary file into a Hashtable of objects ht.Clear();  // Clear hash table. fs = new FileStream("c:\\chairs.dat", FileMode.Open); ht =  (Hashtable) bf.Deserialize(fs); // Confirm objects properly recreated ch = (Chair)ht["11-19"]; Console.WriteLine(ch.myUpholstery.Fabric);  // "Silk" fs.Close(); 

Observe the following key points:

  • The serialization and IO namespaces should be declared.

  • The Chair and Upholstery classes must have the [Serializable] attribute; otherwise, a runtime error occurs when Serialize()is executed.

  • Serialization creates an object graph that includes references from one object to another. The result is a deep copy of the objects. In this example, the myUpholstery field of Chair is set to an instance of the Upholstery class before it is serialized. Serialization stores a copy of the object rather than a reference. When deserialization occurs, the Upholstery object is restored.

Excluding Class Members from Serialization

You can selectively exclude class members from being serialized by attaching the [NonSerialized] attribute to them. For example, you can prevent the myUpholstery field of the Chair class from being serialized by including this:

 [NonSerialized] public Upholstery myUpholstery; 

The primary reason for marking a field NonSerialized is that it may have no meaning where it is serialized. Because an object graph may be loaded onto a machine different from the one on which it was stored, types that are tied to system operations are the most likely candidates to be excluded. These include delegates, events, file handles, and threads.

Core Note

A class cannot inherit the Serializable attribute from a base class; it must explicitly include the attribute. On the other hand, a derived class can be made serializable only if its base class is serializable.


Binary Serialization Events

.NET 2.0 introduced support for four binary serialization and deserialization events, as summarized in Table 4-9.

Table 4-9. Serialization and Deserialization Events

Event

Attribute

Description

OnSerializing

[Serializing]

Occurs before objects are serialized. Event handler is called for each object to be serialized.

OnSerialized

[Serialized]

Occurs after objects are serialized. Event handler is called once for each object serialized.

OnDeserializing

[Deserializing]

Occurs before objects are deserialized. Event handler is called once for each object to be deserialized.

OnDeserialized

[Deserialized]

Occurs after objects have been deserialized. Event handler is called for each deserialized object.


An event handler for these events is implemented in the object being serialized and must satisfy two requirements: the attribute associated with the event must be attached to the method, and the method must have this signature:

 void <event name>(StreamingContext context) 

To illustrate, here is a method called after all objects have been deserialized. The binary formatter iterates the list of objects in the order they were deserialized and calls each object's OnDeserialized method. This example uses the event handler to selectively update a field in the object. A more common use is to assign values to fields that were not serialized.

 public class Chair    {       // other code here    [OnDeserialized]    void OnDeserialized(StreamingContext context)    {       // Edit vendor name after object is created       if (MyVen == "Lane") MyVen = "Lane Bros.";    } } 

Note that more than one method can have the same event attribute, and that more than one attribute can be assigned to a method although the latter is rarely practical.

Handling Version Changes to a Serialized Object

Suppose the Chair class from the preceding examples is redesigned. A field could be added or deleted, for example. What happens if one then attempts to deserialize objects in the old format to the new format? It's not an uncommon problem, and .NET offers some solutions.

If a field is deleted, the binary formatter simply ignores the extra data in the deserialized stream. However, if the formatter detects a new field in the target object, it throws an exception. To prevent this, an [OptionalField] attribute can be attached to the new field(s). Continuing the previous example, let's add a field to Chair that designates the type of wood finish:

 [OptionalField] private string finish; 

The presence of the attribute causes the formatter to assign a default null value to the finish field, and no exception is thrown. The application may also take advantage of the deserialized event to assign a value to the new field:

 void OnDeserialized(StreamingContext context) {    if (MyVen == "Lane") finish = "Oak"; else finish = "Cherry"; } 

     < Day Day Up > 


    Core C# and  .NET
    Core C# and .NET
    ISBN: 131472275
    EAN: N/A
    Year: 2005
    Pages: 219

    flylib.com © 2008-2017.
    If you may any questions please contact us: flylib@qtcs.net