|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Services
Chapters
Feature Zones
|
Note: This is an unedited contribution. If this article is inappropriate,
needs attention or copies someone else's work without reference then please
Report This Article
IntroductionSerializing and deserializing an object, or an entire graph of connected objects, is a common task. Serialization as a concept is deeply built into the .net framework. Concrete formatters come in different flavors designed for different environments and their requirements. That's the reason why the BackgroundAfter writing the boilerplate serialization code in different flavors again and again, doing basically the same just using different formatters or a like, I came up with the idea to design an extensible serialization "framework". PrerequisitesLet me first introduce the [Serializable]
public class DataStore : Raccoom.Runtime.Serialization.ComponentModel.ISerializerCallback
{
public string Name { get; set; }
public string Location { get; set; }
#region ISerializeableObject Members
void Raccoom.Runtime.Serialization.ComponentModel.ISerializerCallback.Serializing()
{
Console.WriteLine("Serializing");
}
void Raccoom.Runtime.Serialization.ComponentModel.ISerializerCallback.Serialized()
{
Console.WriteLine("Serialized");
}
void Raccoom.Runtime.Serialization.ComponentModel.ISerializerCallback.Deserialized()
{
Console.WriteLine("Deserialized");
}
#endregion
}
Raw like SushiThis is the code needed to flush the System.Security.Cryptography.SymmetricAlgorithm symAlgo = System.Security.Cryptography.Rijndael.Create();
//
System.Xml.Serialization.XmlSerializer xmlSerializer =
new System.Xml.Serialization.XmlSerializer(typeof(DataStore));
DataStore ds = new DataStore();
ds.Location = "HDD";
ds.Name = "IsolatedStorage";
//
using (Stream stream =
new System.IO.FileStream("test.xml", FileMode.Create, FileAccess.Write))
using (DeflateStream compressStream =
new DeflateStream(stream, CompressionMode.Compress))
using (CryptoStream cryptoStream = new CryptoStream(compressStream,
symAlgo.CreateEncryptor(),
System.Security.Cryptography.CryptoStreamMode.Write))
{
xmlSerializer.Serialize(cryptoStream, ds);
}
//
using (System.IO.Stream stream =
new System.IO.FileStream("test.xml", FileMode.Open, FileAccess.Read))
using (System.IO.Compression.DeflateStream compressStream =
new DeflateStream(stream, CompressionMode.Decompress))
using (CryptoStream cryptoStream = new CryptoStream(compressStream,
symAlgo.CreateDecryptor(),
System.Security.Cryptography.CryptoStreamMode.Read))
{
ds = xmlSerializer.Deserialize(cryptoStream) as DataStore;
}
The above example is writing encrypted and compressed xml to the file test.xml. So far so good, everything is up and running and the code works fine. But imagine you have to change the format from xml to binary or soap? Furthermore you don't need compression any more (1TB HDDs are coming our way these days ). What matters now is how many code changes you have to apply and how predictable and reliable the resulting change will work, right? Serializer foundationInitially started to ease serialization with a class that handles the boilerplate code to use different available formatters (custom formatters here at cp[^]) in a unified way I realized that there are more related topics that this class should cover. I already wrote specialized serialization classes to export datasets encrypted or export images compressed. Fortunately encryption and compression features are implemented as Key features
RemarksDespite the unified handling this serializer class provides for any type of formatter the .net world comes up with, your class types still need to implement the underlying formatter specific requirements like attributes, interfaces and special constructors before they can be successfully serialized/deserialized. Architecture
A full blown technical reference can be browsed here [^]
Using the codeThe following code sections show how to use the serializer. The Security goes first... System.Security.Cryptography.SymmetricAlgorithm symAlgo
= System.Security.Cryptography.Rijndael.Create();
Create a Serializer for GZip compressed xml, create a ISerializer<DataStore> xmlGzip = SerializerFactory.CreateXmlSerializerGZip<DataStore>();
Console.WriteLine(xmlGzip);
//
DataStore dataStore = xmlGzip.CreateInstance();
dataStore.Name = "HDD";
dataStore.Location = "IsolatedStorage";
//
xmlGzip.Serialize("datastore.gip", dataStore, symAlgo);
dataStore = xmlGzip.Deserialize("datastore.gip", true, symAlgo);
Create a serializer for Deflate compressed xml and serialize/deserialize ISerializer<DataStore> xmlDeflate = SerializerFactory.CreateXmlSerializerDeflate<DataStore>();
Console.WriteLine(xmlDeflate);
//
xmlDeflate.Serialize("datastore.def", dataStore, symAlgo);
dataStore = xmlDeflate.Deserialize("datastore.def", true, symAlgo);
Create a binary serializer that doesn't support compression or encryption and serialize/deserialize ISerializer<DataStore> binary = SerializerFactory.CreateBinarySerializer<DataStore>();
Console.WriteLine(binary);
//
binary.Serialize("datastore.bin", dataStore);
dataStore = binary.Deserialize("datastore.bin", true);
Create a serializer for GZip compressed soap and serialize/deserialize ISerializer<DataStore> soapDeflate = SerializerFactory.CreateSoapSerializerGZip<DataStore>();
Console.WriteLine(soapDeflate);
//
soapDeflate.Serialize("datastore.soap", dataStore, symAlgo);
dataStore = soapDeflate.Deserialize("datastore.soap", true, symAlgo);
Are there still the expected values? System.Diagnostics.Debug.Assert(dataStore.Location == "IsolatedStorage");
System.DiSystem.Diagnostics.Debug.Assert(dataStore.Name == "HDD");
In fact the above code does more than meets the eye Predictable, reliable, maintainable
ExtensibilityAs a proof of concept I've implemented a custom formatter written by Patrick Boom [^] into the console demo assembly. The original class signature looks like this public sealed class XmlFormatter : IFormatter{}
The class must implement the interface public sealed class XmlFormatter :
IFormatter,Raccoom.Runtime.Serialization.ComponentModel.ISerializerFormatter
{
#region ISerializerFormatter Members
public XmlFormatter() { }
void ISerializerFormatter.Serialize(Stream stream, object instance)
{
this.Serialize(stream, instance);
}
object ISerializerFormatter.Deserialize(Stream stream, Type[] extraTypes)
{
return this.Deserialize(stream, extraTypes[0]);
}
#endregion
}
Basically the formatter would work but for convenience we should provide a factory. public sealed class XmlFormatterFactory
{
public static ISerializer
Points of InterestDeploymentThe software is also available as a ClickOnce setup. The technical reference can be browsed here BackstageThe whole project presented here was made in Visual Studio 2008 Beta 2. For coding purpose it really worked like a RTM and never crashed even tough it's a feature complete beta version. Only the HTML Editor to write this article sometimes crashed.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||