Using protobuf-net to serialize objects without hassle

Serialization with protobuf-net

You are looking at revision 16 of this page, which may be out of date. View the latest version.  

protobuf-net is an open source .net implementation of Google's protocol buffer binary serialization format.

Setting it up

Consider the following classes, I have included a sub-class to make it slightly more than a non-trivial example

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
    public DateTime DateOfBirth { get; set; }
    public Address Address { get; set; }
}

public class Address
{
    public string Number { get; set; }
    public string StreetName { get; set; }
}

public class ExtendedAddress : Address
{
    public string BuildingName { get; set; }
}

With v2 of protobuf-net you can build a serializer on the fly which means you don't have to decorate the classes with attributes:

var protobufModel = ProtoBuf.Meta.TypeModel.Create();
AddTypeToModel<Person>(protobufModel);
AddTypeToModel<Address>(protobufModel).AddSubType(500, typeof(ExtendedAddress));
AddTypeToModel<ExtendedAddress>(protobufModel);

private MetaType AddTypeToModel<T>(RuntimeTypeModel typeModel)
{            
    var properties = typeof(T).GetProperties().Select(p => p.Name).ToArray();
    return typeModel.Add(typeof(T), true).Add(properties);            
}

While this method is effective it is considered brittle if the class changes. What would happen if we added a Lot property to the Address class? This would break the backwards compatibility of any previously serialized data as protobuf-net is assigning ids to each serialized field.

If we added a new property Suburb which is alphabetically after StreetName then backwards compatibility would be maintained albeit by sheer luck.

The moral of the story is only use this method if your class isn't going to change (sure!) or you dont care about backwards compatibility.

Instead, decorate your classes:

[ProtoContract]
public class Person
{
    [ProtoMember(1)]
    public string Name { get; set; }
    [ProtoMember(2)]
    public int Age { get; set; }
    [ProtoMember(3)]
    public DateTime DateOfBirth { get; set; }        
    [ProtoMember(4)]        
    public Address Address { get; set; }
}

[ProtoContract]
[ProtoInclude(500, typeof(ExtendedAddress))]
public class Address
{
    [ProtoMember(1)]
    public string Number { get; set; }
    [ProtoMember(2)]
    public string StreetName { get; set; }
}

[ProtoContract]
public class ExtendedAddress : Address
{
    [ProtoMember(1)]
    public string BuildingName { get; set; }
}

protobuf-net also respects DataContract and DataMember attributes in the System.Runtime.Serialization namespace if you want to use them instead or if you class is attributed already.

Speed and size

Using AutoFixture I was able to create a Person object to test serialization with just a couple of lines of code that looks like this:

alt text

The following is a summary of speed and size of the serialized data compared to the built-in BinaryFormatter. Tests conducted on an i7 870 @ 2.93GHz running Windows 7 on .Net4.0.

Size (bytes)Speed* (ms)
BinaryFormatter10243132
protobuf-net256458

*Serialization time is for 100,000 iterations. 1 iteration is a serialization followed by a deserialization

protobuf-net is a clear winner in both size (75% smaller) and speed (85% faster)

Posted by: Wallace Turner
Last revised: 03 Nov, 2011 03:05 PM History
You are looking at revision 16 of this page, which may be out of date. View the latest version.

Comments

No comments yet. Be the first!

No new comments are allowed on this post.