The FCL contains several classes to allow objects to be serialized into different formats. Choosing the correct format for your task and remembering how to use that format can become a chore, especially when there is a mixture of different formats and all of them are on disk. You need some way of simplifying the serialization interfaces to make serialization easy without worrying about the underlying differences in the serialization classes. This will also allow other developers on your team to become proficient with the use of the various serializers more quickly.
Use the façade design pattern
to create the following
Serializer
class:
using System; using System.Collections; using System.IO; using System.Runtime.Serialization.Formatters.Binary; using System.Xml.Serialization; using System.Runtime.Serialization.Formatters.Soap; // Note that you must also add a reference to the following assembly: // System.Runtime.Serialization.Formatters.Soap.dll [Serializable] public class Serializer { public Serializer( ) {} protected Hashtable serializationMap = new Hashtable( ); protected Hashtable serializationTypeOfMap = new Hashtable( ); // Serialize an object public void SerializeObj(object obj, string destination) { SerializeObj(obj, destination, SerializationAction.Default); } public void SerializeObj( object obj, string destination, SerializationAction action) { if (action == SerializationAction.RetainAssemblyInfo || action == SerializationAction.RetainPrivateMembers || action == SerializationAction.SmallestFootprint || action == SerializationAction.Default) { BinarySerializeObj(obj, destination); serializationMap.Add(destination.ToUpper( ), DeserializationType.Binary); } else if (action == SerializationAction.MakePortable || action == SerializationAction.AsSOAPMsg) { SoapSerializeObj(obj, destination); serializationMap.Add(destination.ToUpper( ), DeserializationType.SOAP); } else if (action == SerializationAction.AsXML || action == SerializationAction.SendToXMLWebService) { XmlSerializeObj(obj, destination); serializationMap.Add(destination.ToUpper( ), DeserializationType.XML); serializationTypeOfMap.Add(destination.ToUpper( ), obj.GetType( ).FullName); } } private void BinarySerializeObj(object obj, string destination) { BinaryFormatter binFormatter = new BinaryFormatter( ); Stream fileStream = new FileStream(destination, FileMode.Create, FileAccess.Write, FileShare.None); binFormatter.Serialize(fileStream, obj); fileStream.Close( ); } private void SoapSerializeObj(object obj, string destination) { SoapFormatter SOAPFormatter = new SoapFormatter( ); Stream fileStream = new FileStream(destination, FileMode.Create, FileAccess.Write, FileShare.None); SOAPFormatter.Serialize(fileStream, obj); fileStream.Close( ); } private void XmlSerializeObj(object obj, string destination) { XmlSerializer XMLFormatter = new XmlSerializer(obj.GetType( )); Stream fileStream = new FileStream(destination, FileMode.Create, FileAccess.Write, FileShare.None); XMLFormatter.Serialize(fileStream, obj); fileStream.Close( ); } // DeSerialize an object public object DeSerializeObj(string source) { return (DeSerializeObj(source, (DeserializationType)serializationMap[source.ToUpper( )])); } public object DeSerializeObj(string source, DeserializationType type) { object retObj = null; if (type == DeserializationType.Binary) { retObj = BinaryDeSerializeObj(source); serializationMap.Remove(source.ToUpper( )); } else if (type == DeserializationType.SOAP) { retObj = SoapDeSerializeObj(source); serializationMap.Remove(source.ToUpper( )); } else if (type == DeserializationType.XML) { retObj = XmlDeSerializeObj(source); serializationMap.Remove(source.ToUpper( )); serializationTypeOfMap.Remove(source.ToUpper( )); } return (retObj); } private object BinaryDeSerializeObj(string source) { BinaryFormatter binFormatter = new BinaryFormatter( ); Stream fileStream = new FileStream(source, FileMode.Open, FileAccess.Read, FileShare.None); object DeserializedObj = binFormatter.Deserialize(fileStream); fileStream.Close( ); return (DeserializedObj); } private object SoapDeSerializeObj(string source) { SoapFormatter SOAPFormatter = new SoapFormatter( ); Stream fileStream = new FileStream(source, FileMode.Open, FileAccess.Read, FileShare.None); object DeserializedObj = SOAPFormatter.Deserialize(fileStream); fileStream.Close( ); return (DeserializedObj); } private object XmlDeSerializeObj(string source) { XmlSerializer XMLFormatter = new XmlSerializer(Type.GetType((string)serializationTypeOfMap [source.ToUpper( )])); Stream fileStream = new FileStream(source, FileMode.Open, FileAccess.Read, FileShare.None); object DeserializedObj = XMLFormatter.Deserialize(fileStream); fileStream.Close( ); return (DeserializedObj); } } public enum SerializationAction { Default = 0, RetainAssemblyInfo, RetainPrivateMembers, MakePortable, SmallestFootprint, SendToXMLWebService, AsSOAPMsg, AsXML } public enum DeserializationType { Binary = 0, SOAP, XML }
The façade design pattern uses a façade class to provide a simple interface to a group of underlying objects that do similar work. Any client that wants to use one of the underlying objects can go through the façade object. In effect, the façade pattern abstracts away the complexities and disparities between the underlying classes. This allows a uniform, and much easier to use, interface to be presented to the clients that wish to use any of these underlying objects.
The façade object can decide which underlying object will be used to perform the action requested, but it is not required to do so. The user could even pass in one or more arguments allowing the façade object to determine which underlying object to use. The nice thing about this pattern is that if the client decides that they need more flexibility than is provided by the façade object, they can choose to use the underlying objects and contend with their individual complexities. Also, if other serialization classes are created, they can easily be added to the façade object without breaking the existing code.
The class that acts as the façade in this recipe is the
Serializer
class. This class abstracts away the
various interfaces to the various serializers that ship with the FCL,
namely:
System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
System.Xml.Serialization.XmlSerializer
|
System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
In addition, this class provides an enumeration called
SerializationAction
, which can be passed to the
SerializeObj
method for the
Serializer
to choose the best type of
serialization object to use to serialize the input data. The various
values of the SerializationAction
enumeration and
their meanings are:
Default
Uses the default serialization object, which is
BinaryFormatter
.
RetainAssemblyInfo
Uses the BinaryFormatter
. This value is used when
the assembly information needs to be retained by the serialization
process.
RetainPrivateMembers
Uses the BinaryFormatter
. This value is used when
private members need to be added to the serialization stream.
SmallestFootprint
Uses the BinaryFormatter
. This value is used when
the client wants the serialization data in the most compact form
possible. As an added benefit, this serialization method is also the
fastest.
MakePortable
Uses the SoapFormatter
. This value is used when
the serialization data needs to be in the most portable form (i.e.,
SOAP).
AsSOAPMsg
Uses the SoapFormatter
. This value tells the
façade object to explicitly use the
SoapFormatter
.
SendToXMLWebService
Uses the XmlSerializer
. This value is used when
the serialized object will be sent to an ASP.NET XML web service.
AsXML
Uses the XmlSerializer
. This value tells the
façade object to explicitly use the
XmlSerializer
.
The interface to the Serializer
object contains
two sets of overloaded methods: SerializeObj
and
DeSerializeObj
. Both
SerializeObj
methods accept an object to be
serialized in the obj
parameter and a
location to store the serialized object in the
destination
parameter. The second
SerializeObj
method also has a parameter that
accepts a SerializationAction
enumeration, which
was previously discussed. The first SerializeObj
method does not have this parameter and so defaults to using the
SerializationAction.Default
enumeration value.
You need to have permissions to open a FileStream
directly from your code in order to use this recipe. This recipe
cannot be used in a partial-trust environment where you are obliged
to get your FileStream
s either from
IsolatedStorage
or from a
FileDialog
.
Both DeSerializeObj
methods accept a source string
indicating where the serialized object is located. The second
overloaded DeSerializeObj
method also accepts a
DeserializationType
enumeration. This enumeration
contains three values—Binary
,
SOAP
, and XML
—and is used
to explicitly inform the underlying Deserialize
methods of which serialization objects to use. If the first
DeSerializeObj
method is called, the values cached
in the SerializeObj
methods are used to
deserialize the object without the client having to remember various
small details about the serialization process used to initially
serialize the object. If the SerializeObj
methods
are not used to serialize the object, one of the various
DeserializationType
enumeration values can be
explicitly passed as an argument to inform the
DeSerializeObj
method which underlying
deserialization method to call.
The serializationMap
and
serializationTypeOfMap
Hashtables
are used to cache various pieces of
information during the serialization process. The
SerializeObj
methods use the
serializationMap
Hashtable
to
map the destination of the serialized object to the type of
serialization process used. This allows the
DeSerializeObj
methods to use the source parameter
to locate the pertinent information in the
serializationMap
Hashtable
. The
serializationTypeOfMap
is used only when the
XmlSerializer
object is used for serialization.
Upon deserialization, the XmlSerializer
uses the
serializationTypeOfMap
to locate the full type
name that is to be deserialized.
The following code serializes an integer array to the file
TestBinSerXML.txt
and then deserializes it into
the retArray
variable:
Serializer s = new Serializer( ); s.SerializeObj(new int[10] {1,2,3,4,5,6,7,8,9,10}, @"C:TestBinSerXML.txt", SerializationAction.AsXML); int[] retArray = (int[])s.DeSerializeObj(@"c:TestBinSerXML.txt");