Writing objects to files – Serialization
The Admin 7/31/2011 1:47:00 PMIn this example I’m going to show how to store objects (instances of a class) in a file that can then later be read again. In this example we’ll be storing the objects in a binary file, in another example I’ll show how to use XML files.
But what is Serialization?
Serialization is the process of converting complex objects into a stream of data (bytes or text) for storage. Deserialization is its reverse process, that is unpacking a stream to it’s original form.
Let’s Start!
For this example to run you need to add the following to the top of the .cs file:
using System.IO; using System.Runtime.Serialization.Formatters.Binary;
The System.IO namespace contains types that allow reading and writing to files and data streams, and types that provide basic file and directory support, while the the System.Runtime.Serialization.Formatters.Binary namespace contains the BinaryFormatter class, which can be used to serialize and deserialize objects to/from binary format.
The Example also needs a definition of the Book class, notice that the class is marked as Serializable so that it can be serialized – that’s about all you need to do with a basic class to make it serializable (basic as in all member variables are public). A more Object Oriented version of the class can be found later in the example …
[Serializable]
class Book
{
public string Title;
public string Author;
}
Code
Book b = new Book();
b.Title = "Learning C#";
b.Author = "Jon";
FileStream fs = new FileStream("books.dat", FileMode.CreateNew);
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(fs, b);
fs.Close();
Explanation
First we start by creating an instance of the Book class and give it’s members some values
Book b = new Book(); b.Title = "Learning C#"; b.Author = "Jon";
We then create a FileStream to a file that we want to write to, notice that I use the FileMode.CreateNew which would throw an exception if the file already exists. To read more about FileStreams check out Writing text to files.
FileStream fs = new FileStream("books.dat", FileMode.CreateNew);
We then create an instance of the BinaryFormatter class which we then use to serialize the object (the book) to the FileStream (into the file called books.dat). Note that the Serialize method accepts two parameters, the stream to which it shall write and the object to serialize. The stream could be any type of stream, so we could use the same method (different type of stream though) to send data over the Internet !
BinaryFormatter bf = new BinaryFormatter(); bf.Serialize(fs, b);
We then make sure to close the stream.
fs.Close();
The data that gets stored in the binary file is the instance of the Book class I just created. The binary file also stores information about which version of which program actually wrote the file, so that we can make sure that we can read the file correctly again.
This can cause problems if we want another program to be able to read the binary file (deserialize the data), but that can be easily fixed by putting the class (or classes) that were serialized in a class library (dll file).
If I had wanted to store many books in this binary file the easiest way would have been to use a generic list to create a list of books which I then could serialize.
Book b1 = new Book(); b1.Title = "Learning C#"; b1.Author = "Jon"; Book b2 = new Book(); b2.Title = "Learning WPF"; b2.Author = "Jon"; Listbooks = new List (); books.Add(b1); books.Add(b2); FileStream fs = new FileStream("books.dat", FileMode.CreateNew); BinaryFormatter bf = new BinaryFormatter(); bf.Serialize(fs, books); fs.Close();
But what if the class changes between versions of the program ? Can we still read the old data ? Well .NET has something called Version Tolerant Serialization, which makes it possible.
To read the data (the instance of the book class) from the file we use the Deserialize method of the BinaryFormatter class. The Deserialize method takes one parameter, the stream the object is supposed to be read from.
The Deserialize method returns an object, which means we need to Type-Cast it to the right kind of a class (or an instance to be precise). This is done by putting the name of the class in front of the method surrounded by parenthesis.
If we were reading (loading) a list of books from the file that line would have been changed to this:
And lastly a “Better” Version of the Book Class
More about the ISerializable interface and what happens when you inherit a serializable class can be found here Implement ISerializable correctly
What about deserialization ?
FileStream fs = new FileStream("books.dat", FileMode.Open);
BinaryFormatter bf = new BinaryFormatter();
Book b = (Book)bf.Deserialize(fs);
fs.Close();
Console.WriteLine(b.Title + " by " + b.Author);
Book b = (Book)bf.Deserialize(fs);
List
[Serializable]
class Book : ISerializable
{
private string _Title;
private string _Author;
public Book(string Title, string Author)
{
this._Title = Title;
this._Author = Author;
}
public string Author
{
get { return _Author; }
set { _Author = value; }
}
public string Title
{
get { return _Title; }
set { _Title = value; }
}
public Book(SerializationInfo info, StreamingContext ctxt)
{
_Author = (string)info.GetValue("Author", typeof(string));
_Title = (string)info.GetValue("Title", typeof(string));
}
public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
{
info.AddValue("Author", _Author);
info.AddValue("Title", _Title);
}
}
As you can see I’ve added two constructors one that initializes the data members which are now private and only accessable via the Title and Author properties (again via get and set methods) and another that is used for deserialization.
The class now implements the ISerializable interface as the member variables are now private and only accessable inside of the class itself !
Let’s take a look at what has been added (for the serialization):
class Book : ISerializable
The class implements the ISerializable interface
The class needs to implement the GetObjectData method which is called when the object is serialized. We use that method to store information about the private member variables the class holds
public void GetObjectData(SerializationInfo info, StreamingContext ctxt)
{
info.AddValue("Author", _Author);
info.AddValue("Title", _Title);
}
The Book class now needs a constructor that is used when deserializing the object of this type. The constructor actually does the reverse of what was done in the GetObjectData method !
public Book(SerializationInfo info, StreamingContext ctxt)
{
_Author = (string)info.GetValue("Author", typeof(string));
_Title = (string)info.GetValue("Title", typeof(string));
}
Disclaimer: I didn't author this article, it is on my blog as an aide-memoire for me. Here is the original Source.


The Admin says:
7.31.2011 at 2:36 PMNice