Lesson 10 - Serialization and deserialization in C# .NET
In the previous lesson, LINQ to XML in C# .NET, we introduced the LINQ to XML technology. In today's tutorial, we're going to talk about serialization and deserialization.
This article was written by Jan Vargovsky.
Serialization means preserving an object's state. A bit more scientifically, it could be described as converting an object to a stream of bytes and then storing it somewhere in memory, database, or a file. Deserialization is the opposite of serialization. One could say it's a conversion of the byte stream back to the object copy.
What is it good for?
Serialization allows us to save the object state and then, thanks to deserialization, recreate it once more. Serialization is used to do things like send data through a network or save program settings.
Sample application
Let's create a new project of the Windows Forms Application
type. Then, we'll add a class whose instances we'd like to preserve and restore
to the state in which they were when the application was closed. We'll name the
class User
and add the FirstName, LastName, and BirthDate
properties to it. The class might look something like this (note that it's
public):
public class User { public string FirstName { get; set; } public string LastName { get; set; } public DateTime BirthDate { get; set; } }
On the main form, let's add two TextBoxes for the first and last names. We'll also add a DateTimePicker control so we could specify the user's birth date. Next, we'll add a button somewhere which we'll use to add users to our application. We'll add a ListBox control for displaying users. Finally, we'll rename the controls from their default names to something that will help us differentiate them:
Next, we'll create a collection of the List<User> type so we have something to store our users into. Let's move to the main form (Form1) and add a private collection to its class.
private List<User> users = new List<User>();
Move back to the form designer and add a method to the Click event on the addButton control. We'll add the code for adding a user to our collection. Furthermore, we also have to show the users in the usersListBox. We'll use the DataSource property to do so.
private void addButton_Click(object sender, EventArgs e) { // Creates a new user using the data from the controls User user = new User { FirstName = firstNameTextBox.Text, LastName = lastNameTextBox.Text, BirthDate = birthDateDateTimePicker.Value }; // Adds the user to our collection users.Add(user); // Refreshes the data source of our usersListBox usersListBox.DataSource = null; usersListBox.DataSource = users; }
Now we have a fairly functional application. When you launch it and try to add a user, you'll see that it actually adds a "ProjectName.User" item. Let's move on to the User source file and override the ToString() method. Here's what you'll need to change it to:
public override string ToString() { return "First name: " + FirstName + " Last name: " + LastName + " Birth Date: " + BirthDate.ToShortDateString(); }
Try to add several users and see just how much more human-readable they are.
Serialization
Now, we can finally move to data serialization. Let's create a Serialize() method in the form's code-behind.
private void Serialize() { try { // Creates XmlSerializer of the List<User> type XmlSerializer serializer = new XmlSerializer(users.GetType()); // An alternative syntax could also be: //XmlSerializer serializer = new XmlSerializer(typeof(List<User>)); // Creates a stream using which we'll serialize using (StreamWriter sw = new StreamWriter("users.xml")) { // We call the Serialize() method and pass the stream created above as the first parameter // The second parameter is the object which we want to serialize serializer.Serialize(sw, users); } } catch (Exception ex) { MessageBox.Show(ex.Message); } }
We used the serializer for the XML format. There are several types of serializers including the binary one which .NET framework provides. In fact, there's no need to worry about anything since instances are serialized automatically. Let's go back to the form designer and find the OnClosing event, where we'll double click and call our Serialize() method in the handler code.
private void Form1_FormClosing(object sender, FormClosingEventArgs e) { Serialize(); }
Note: This method's name may vary depending on the name of the main form. If you didn't modify it, it should all look the same.
If we start the program, add several users and close it, a collection of the
users will be serialized and stored in
ProjectName/bin/Debug/users.xml
. When we open the file, it should
be readable. For me, the file looks like this:
<?xml version="1.0" encoding="utf-8"?> <ArrayOfUser xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <User> <FirstName>John</FirstName> <LastName>Smith</LastName> <BirthDate>2013-07-11T17:27:19</BirthDate> </User> <User> <FirstName>James</FirstName> <LastName>Brown</LastName> <BirthDate>2013-07-11T17:27:19</BirthDate> </User> </ArrayOfUser>
Deserialization
The serializing part is now done, so let's move on to deserialization. From a
coding point of view, it's a bit more complex, so we'll go through the entire
process again. We'll start by creating a Deserialize() method in the main form's
code. Then, we'll need to determine whether the XML file with the data even
exists. The File class and its Exists(string path) bool method
will help us with that. In the condition's body, we'll create an
XmlSerializer instance which will be of the same type as our
users
List. Once that's done, we'll create the
StreamReader instance with a path to our file and then simply
call the Deserialize() method on the XmlSerializer. However,
there is a small catch, the Deserialize() method returns an
object. Meaning that we'll have to cast it
before we assign our saved users to our existing ones. The whole method, with
all of this in mind, looks is as follows:
private void Deserialize() { try { if (File.Exists("users.xml")) { XmlSerializer serializer = new XmlSerializer(users.GetType()); using (StreamReader sr = new StreamReader("users.xml")) { users = (List<User>)serializer.Deserialize(sr); } } else throw new FileNotFoundException("File not found"); } catch (Exception ex) { MessageBox.Show(ex.Message); } }
We'll call this method in the form's Load event. Now, let's move to the designer, find the Load event (in the form's properties), and create its handler method. We'll call our Deserialize() method in it and load the users to our ListBox. The entire method looks like this:
private void Form1_Load(object sender, EventArgs e) { Deserialize(); usersListBox.DataSource = users; }
Note: This method's name may also vary just like the previous handler method.*
When you open the application, add data, close it, and open it again. It should include all of the users that you added before.
The conclusion
In conclusion, I'd like to mention a few things which you'd probably find out soon or later when serializing objects for your applications.
- The class being serialized must have a parameterless constructor (or a non-parametric constructor). This is because the first thing the deserializer does is create an empty instance, and then gradually assigns its properties as it reads them from the file (or from any other stream).
- You can't serialize controls, not the .NET ones nor your own (User Controls). There's no need to serialize everything. Only save the things that you really need.
- Serialization includes several attributes, such as:
- [XmlIgnore] - Keeps it from serializing the property.
- [Serializable()] - Marks the class clearly so that it will be serialized. (Implements the ISerializable interface). We can place this one right above the class declaration.
- [XmlAttribute("Name")] changes the XML element from paired to unpaired,
and puts the value of the property in the attribute. For example,
<User FirstName="John">
rather than<FirstName>John</FirstName>
.
- If you've ever wanted to serialize the Color class, it can be serialized. However, it will have no value in the file. You may serialize it as a hexadecimal number, and when you deserialize it you can convert it back to a color.
In the next lesson, we'll work with binary files.
Did you have a problem with anything? Download the sample application below and compare it with your project, you will find the error easily.
Download
By downloading the following file, you agree to the license terms
Downloaded 49x (59.9 kB)
Application includes source codes in language C#