Section 9.2. Serialization Formatters


9.2. Serialization Formatters

It's up to the client to decide which stream type to use for serialization and in which format the data should be represented. A formatter is an object that implements the IFormatter interface, defined in the System.Runtime.Serialization namespace:

     public interface IFormatter     {        object Deserialize(Stream serializationStream);        void Serialize(Stream serializationStream, object graph);        /* Other methods */     }

IFormatter's significant methods are Serialize( ) and Deserialize( ), which perform the actual serialization and deserialization. .NET ships with two formatters: a binary formatter and a Simple Object Access Protocol (SOAP) formatter. The binary formatter generates a compact binary representation of the object's state. It's relatively fast to serialize an object into binary format and to deserialize an object from binary format. The SOAP formatter, as the name implies, persists the object's state using a text-based XML format. Naturally, the SOAP formatter is considerably slower than the binary formatter, because serialization requires composing a SOAP envelope and deserialization requires SOAP parsing. In addition, the resulting serialization data (i.e., the file size or memory footprint) is bigger. The only advantage of using the SOAP formatter is that it's platform-neutral: you can provide the serialization information (via network stream or file access) to applications running on non-Windows-based platforms, and they can deserialize equivalent objects on their side or serialize their objects back to the .NET side. In general, unless cross-platform interoperability is required, you should always use the binary formatter.

9.2.1. The Binary Formatter

The binary formatter is the class BinaryFormatter, defined in the System.Runtime.Serialization.Formatters.Binary namespace. To serialize an object with the binary formatter, create a stream object (such as a file stream) and call the Serialize( ) method of the formatter, providing the formatter with the object to serialize and the stream to serialize it to. When you are done with the stream, remember to dispose of it. Example 9-2 shows how to serialize an object of type MyClass to a file stream.

Example 9-2. Binary serialization using a file stream
 using System.Runtime.Serialization.Formatters.Binary; MyClass obj = new MyClass(  ); IFormatter formatter = new BinaryFormatter(  ); //Creating a stream Stream stream; stream = new FileStream(@"C:\temp\obj.bin",FileMode.Create,FileAccess.Write); using(stream) {    formatter.Serialize(stream,obj); }

To deserialize an object, you need to open the appropriate stream (matching the type of stream used for serialization) and call the Deserialize( ) method of the formatter, as shown in Example 9-3. You will receive back an object reference.

Example 9-3. Binary deserialization using a file stream
 using System.Runtime.Serialization.Formatters.Binary; MyClass obj; //No new! IFormatter formatter = new BinaryFormatter(  ); //Opening a stream Stream stream; stream = new FileStream(@"C:\temp\obj.bin",FileMode.Open,FileAccess.Read); using(stream) {    obj = (MyClass)formatter.Deserialize(stream); }

There are a few things worth mentioning regarding deserialization. First, make sure to open a stream on an existing medium, instead of creating a new one and destroying the existing serialized information. Second, note that there is no need to create an object explicitly using new. The Deserialize( ) method creates a new object and returns a reference to it. In fact, during deserialization, no constructor is ever called. Third, Deserialize( ) returns an amorphous object reference, so you need to downcast it to the correct object type being deserialized. If the downcast type is different from the type that was serialized, an exception is thrown. Finally, be sure to dispose of the stream when you are finished.

9.2.2. The SOAP Formatter

The SoapFormatter class, defined in the System.Runtime.Serialization.Formatters.Soap namespace, is used exactly like the BinaryFormatter class. The only difference is that it serializes using a SOAP format instead of binary format. Example 9-4 demonstrates serialization into a file stream using the SOAP formatter.

Example 9-4. SOAP serialization using a file stream
 using System.Runtime.Serialization.Formatters.Soap; //In the MyClassLibrary assembly: namespace MyNamespace {    [Serializable]    public class MyClass    {       public int Number1;       public int Number2;    } } //In the client's assembly: MyClass obj = new MyClass(  ); obj.Number1 = 123; obj.Number2 = 456; IFormatter formatter = new SoapFormatter(  ); Stream stream; stream = new FileStream(@"C:\temp\obj.xml",FileMode.Create,FileAccess.Write); using(stream) {    formatter.Serialize(stream,obj); }

Example 9-5 shows the resulting file content. The deserialization using a SOAP formatter is exactly like that in Example 9-3, except it uses the SOAP formatter instead of the binary one.

Example 9-5. The SOAP format serialization output of Example 9-9
 <SOAP-ENV:Envelope    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    xmlns:xsd="http://www.w3.org/2001/XMLSchema"    xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/"    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"    xmlns:clr="http://schemas.microsoft.com/soap/encoding/clr/1.0"    SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">    <SOAP-ENV:Body>       <a1:MyClass                     xmlns:a1="http://schemas.microsoft.com/clr/nsassem/                     MyNamespace/MyClassLibrary%2C%20                     Version%3D1.0.898.27976%2C%20                     Culture%3Dneutral%2C%20                     PublicKeyToken%3Dnull">          <Number1>123</Number1>          <Number2>456</Number2>       </a1:MyClass>    /SOAP-ENV:Body> </SOAP-ENV:Envelope>

9.2.3. Generic Formatters

The IFormatter interface was defined before generics were available in .NET, but with the introduction of generics in .NET 2.0 you can improve on the available implementations of IFormatter by providing generic and type-safe wrappers around them. You can define the IGenericFormatter interface that provides similar methods to IFormatter, except it uses generic methods:

     public interface IGenericFormatter     {        T Deserialize<T>(Stream serializationStream);        void Serialize<T>(Stream serializationStream,T graph);     }

: .NET XML Serialization

.NET provides an alternate serialization infrastructure, available in the System.Xml.Serialization namespace. XML serialization is similar in concept to the text-based SOAP formatter. The main difference is that with XML serialization, you have granular control over the layout and structure of the serialized information. Such control is often needed when interoperating with other platforms, or when you need to comply with a predefined data schema. Although XML-based serialization is used in much the same way as the serialization support described so far, it does have several limitations: it must operate on public classes; it cannot handle interface references, so no fields should be interface references; and there is no support for dealing with circular references in the object graph. XML-based serialization is oriented toward exchanging data across the Internet with other platforms, while .NET serialization is component-based and oriented toward applications and interacting objects.


Using generic methods is preferable to making the whole interface generic, because it allows you to use the same formatter instance but change the type parameter being serialized or deserialized in every call. You can simply implement IGenericFormatter by encapsulating a non-generic formatter, and delegate the calls to it. Example 9-6 shows the generic class GenericFormatter<F>, which implements IGenericFormatter.

Example 9-6. Implementing IGenericFormatter
 public class GenericFormatter<F> : IGenericFormatter where F : IFormatter,new(  ) {    IFormatter m_Formatter = new F(  );    public T Deserialize<T>(Stream serializationStream)    {       return (T)m_Formatter.Deserialize(serializationStream);    }    public void Serialize<T>(Stream serializationStream,T graph)    {       m_Formatter.Serialize(serializationStream,graph);    } } public class GenericBinaryFormatter : GenericFormatter<BinaryFormatter> {} public class GenericSoapFormatter : GenericFormatter<SoapFormatter> {}

GenericFormatter<F> is defined using the generic type parameter F. F is constrained to implement IFormatter and provide a default constructor. Then, GenericFormatter<F> declares a member of type IFormatter and assigns into it a new F object:

     IFormatter m_Formatter = new F(  );

Example 9-6 also defines two subclasses of GenericFormatter<F>: GenericBinaryFormatter and GenericSoapFormatter. All they do is provide the binary or the SOAP formatter, respectively, as a type parameter to GenericFormatter<F>. You could define GenericBinaryFormatter and GenericSoapFormatter with the using statement, like this:

     using GenericBinaryFormatter = GenericFormatter<BinaryFormatter>;     using GenericSoapFormatter   = GenericFormatter<SoapFormatter>;

but that would have file scope only. In this case, inheritance is good for strong typing and shorthand across files and assemblies.

Example 9-7 demonstrates the use of the generic, type-safe formatters.

Example 9-7. Using the generic formatters
 [Serializable] public class MyClass {...} MyClass obj1 = new MyClass(  ); MyClass obj2; IGenericFormatter formatter = new GenericBinaryFormatter(  ); Stream stream; stream = new FileStream(@"C:\temp\obj.bin",FileMode.Create,FileAccess.ReadWrite); using(stream) {    formatter.Serialize(stream,obj1);    stream.Seek(0,SeekOrigin.Begin);    obj2 = formatter.Deserialize<MyClass>(stream); }

The next version of the serialization infrastructure (a part of Indigo) will contain a generic version of IFormatter similar to IGenericFormatter.


9.2.4. Serialization of Generic Type Parameters

.NET allows you to have serializable generic types:

     [Serializable]     public class MyClass<T>     {...}

If the serializable type is generic, the metadata captured by the formatter contains information about the specific type parameters used. Consequently, each permutation of a generic type with a specific parameter type is considered a unique type. For example, you cannot serialize an object of type MyClass<int> and then deserialize it into an object of type MyClass<string>. Serializing an instance of a generic type is no different from serializing a non-generic type. However, when you deserialize that type, you need to declare a variable with the matching specific type parameter, and you must specify these types again when you call Deserialize( ). Example 9-8 demonstrates serialization and deserialization of a generic type.

Example 9-8. Client-side serialization of a generic type
 [Serializable] public class MyClass<T> {...} MyClass<int> obj1 = new MyClass<int>(  ); MyClass<int> obj2; IGenericFormatter formatter = new GenericBinaryFormatter(  ); Stream stream; stream = new FileStream(@"C:\temp\obj.bin",FileMode.Create,FileAccess.ReadWrite); using(stream) {    formatter.Serialize(stream,obj1);    stream.Seek(0,SeekOrigin.Begin);    obj2 = formatter.Deserialize<MyClass<int>>(stream); }

You cannot use the SOAP formatter to serialize generic types.




Programming. NET Components
Programming .NET Components, 2nd Edition
ISBN: 0596102070
EAN: 2147483647
Year: 2003
Pages: 145
Authors: Juval Lowy

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