Serialization has always been a thorn in my side. In VB6. in Python, in C#. The biggest annoyance is that most applications consist of somewhat complex object relationships and heirarchy’s, different race conditions about values that need to be initialized first, and who knows what else, all meticulously built during the course of your application, which you eventually need to save to persistent storage.
All applications can benefit from some form of persistence, even if it is only in the form of settings. As a result most Object Oriented languages have a way to allow you to “serialize” an object to a stream. In .NET, this is provided by way of the [Serializable] Attribute and the ISerializable interface, which allows you to customize the serialization somewhat.
What is interesting, at least in the case of .NET object serialization, is that you can choose one of a number of “Formatters” which will take the data acquired by the Serialization support framework and format it to one of any number of formats. Personally, I have only used The BinaryFormatter class, which will allow to write or read a serialized stream to or from a stream.
So, what use is serialization? Well, aside from the obvious ability to save the stream to a file, you can also use it in other ways. For example, since you can send the data across any stream, you could send it through a network stream, and on the other end another instance of your application would be able to rebuild the exact same object. Another usage I’ve found is for usage on the system clipboard; serialize what is being copied, plop it on the clipboard with your own format specifier, and then when you want to see if you can paste check if that specifier exists and when pasting deserialize that data from the clipboard, and you’ve got the objects that need to be pasted, which will be “new” objects separate from those copied (if they are still present).
Copying a object graph to stream, however, has a bit of boilerplate; you need to create the BinaryFormatter(), serialize, and the opposite in the other direction.
However, with generics, this task can be made a tad easier:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
public static void ObjectToStream<T>(T saveme, Stream outstream) where T : ISerializable { BinaryFormatter bf = new BinaryFormatter(); using(GZipStream gz = new GZipStream(outstream,CompressionMode.Compress)) { bf.Serialize(gz, saveme); } } public static T StreamToObject<T>(Stream instream) where T : ISerializable { BinaryFormatter bf = new BinaryFormatter(); using(GZipStream gz = new GZipStream(instream,CompressionMode.Decompress)) { return (T)bf.Deserialize(gz); } } |
Two static methods that serve to input and output a object whose type implements ISerializable to and from a given stream. It also uses the GZipStream to compress and decompress that stream to try to save space.
Extending this directly to files is rather easy as well:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public static T ObjectFromFile<T>(String filename) where T : ISerializable { //open the file. T acquireobject; FileStream fstream = new FileStream(filename,FileMode.Open); //input from the stream... acquireobject = StreamToObject(filename); fstream.Close(); } public static ObjectToFile<T>(String filename,T Objectsave) where T:ISerializable { //open the file FileStream fstream = new FileStream(filename,FileMode.Create); ObjectToStream(fstream,Objectsave); fstream.Close(); } |
And huzzah! suddenly you can read and write objects to and from a file with ease.
One might even venture to create extension methods on the Stream class that provide this sort of functionality for input and output of any object supporting ISerializable, like the following, which assumes the previous routines are within a ‘ObjectStreaming’ class definition.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public static class StreamExtensions { public static void WriteObject<T>(this Stream str,T writeobj) where T:ISerializable { ObjectStreaming.ObjectToStream(writeobj, str); } public static T ReadObject<T>(this Stream str) where T : ISerializable { return ObjectStreaming.StreamToObject<t>(str); } } </t> |
And there you have it, suddenly you can write objects directly to any stream, using methods of that stream Object, and that saved data is automatically GZipped for you as well.
This Does, however, present it’s own issues. If there is an error anywhere in your serialization code, using a “filter” type of stream, such as the GZipStream, may cause difficult to trace errors that cause exceptions to fire from the GZipStream itself, most notably “unexpected end of stream” type errors. You can usually trace these in Visual Studio by checking “thrown” on System.Runtime.Serialization.SerializationException in the Debug->Exceptions dialog, and allowing the data to save to a normal stream (that is, directly to a filestream or memory stream rather than by way of the GZipStream). This will allow you to determine where you made the mistake elsewhere. Typically, in my experience, it’s usually something as innocent as a misspelling, or even inconsistent capitalization.
Have something to say about this post? Comment!