.NET Serialisation: Taking a snapshot of your object model

Posted by Tom on 2009-09-27 18:55

The code for this weblog is my own, and while building it I decided I wanted the option to take all of the data and dump it in a manner which would make it easy to restore at a later date (mainly as means of backing up and for testing the installation process.) By this point you are probably already thinking 'serialisation' - the process of flattening out your data for transmission or storage, and its sister: deserialisation - the process of converting this data back into an object.

As you would expect, .NET has plenty in place to help facilitate the process. Here are the basics.

Serialising

There are no boundaries to the format your objects are serialised into, but in ASP.NET two are typically used - XML and JSON. In this example I'll be using XML. To give you an idea of what it looks like, this:

.

serialises to this:

<Comment>
    <CommentId>6</CommentId>
    <PostId>13</PostId>
    <Active>true</Active>
    <Heading>Serialisation Test!</Heading>
    <Body>This is simply some test text.</Body>
    <Poster>Tom</Poster>
    <Email>spam@spam.com</Email>
    <Website></Website>
    <IPAddress>::1</IPAddress>
    <DateCreated>2009-04-28T22:15:42.497</DateCreated>
</Comment>

Before you can even think about serialising your object you need it to have:

And that's pretty much the only hard requirement. When serialised the externally visible properties of the object are included, such as:

Also worth knowing is that you can explicitly state any properties you don't want to serialise using the attributes [XmlIgnore] (for XML serialising) and [ScriptIgnore] (for JSON), which live in the System.Xml.Serialization and System.Web.Script.Serialization namespaces respectively.

Once you've set up your classes you're good to go. If you have a myInstance of MyClass, you do the following:

XmlWriter writer = null;
try
{
    writer = XmlWriter.Create("dump.xml");
    writer.WriteStartDocument();
    XmlSerializer snapshotSerialiser = new XmlSerializer(typeof(MyClass));
    snapshotSerialiser.Serialize(writer, myInstance);
    writer.WriteEndDocument();
}
finally
{
    if (writer != null)
        writer.Close();
}

Deserialising

In theory, this is also the easy bit. .NET makes pretty sure that if you've serialised something, then you can deserialise it again. In practice, and as always, whether or not this process is actually easy depends on the details of your implementation. With that in mind, I'll cover that more in the next section.

The code you need to deserialise looks like a lot like this:

XmlReader reader = XmlReader.Create("dump.xml");
XmlSerializer serializer = new XmlSerializer(typeof(MyClass));
MyClass myInstance = (MyClass)serializer.Deserialize(reader);
reader.Close();

What can we do with it?

Services are the obvious one. So obvious, in fact, that you don't even need to run this stuff explicitly. More useful is importing and exporting data from your object model.

Say I have an object model for a crappy weblog engine that looks something like this:

.

We could then create an object whose sole purpose is to encapsulate your object model. Then you can simply serialise that object to get a snapshot of all the data in your system.

public class Snapshot
{
    public IEnumerable<Content> Contents
    {
        get;
        set;
    }
    
    public IEnumerable<User> Users
    {
        get;
        set;
    }
    
    public Snapshot()
    {
        this.Contents = Content.SelectAll();
        this.Users = User.SelectAll();
    }
    
    public void Restore()
    {
        foreach (Content content in this.Contents)
            content.Restore();
            
        foreach (User user in this.Users)
            user.Restore();
    }
}

The contents of the Restore method will obviously depend on how you persist your objects. In my case it's being saved to an SQL Server database. Since many of the primary keys are identities the IDs may well change once the objects are saved, so I need to account for this. Therefore the Restore method of each object saves the object itself, then saves all its child objects, while updating their referring IDs. The Restore method for the Post class looks like this, for example:

public void Restore()
{
    Save();
    
    foreach (Comment comment in this.Comments)
    {
        comment.PostId = this.PostId;
        comment.Save();
    }
    
    foreach (Tag tag in this.Tags)
    {
        tag.PostId = this.PostId;
        tag.Save();
    }
    
    Refresh();
}

So there you have it - using XML serialisation to save and load your object model. Any questions, corrections or observations, please leave a comment. Unless you're a spambot.