Get up to 80 % extra points for free! More info:

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:

Renaming controls for the serialization sample application in Visual Studio - Files and I/O in C# .NET

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("Na­me")] 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#

 

Previous article
LINQ to XML in C# .NET
All articles in this section
Files and I/O in C# .NET
Skip article
(not recommended)
Binary files in C#
Article has been written for you by David Capka Hartinger
Avatar
User rating:
1 votes
The author is a programmer, who likes web technologies and being the lead/chief article writer at ICT.social. He shares his knowledge with the community and is always looking to improve. He believes that anyone can do what they set their mind to.
Unicorn university David learned IT at the Unicorn University - a prestigious college providing education on IT and economics.
Activities