Facebook Twitter Gplus LinkedIn RSS
formats

SilverlightSerializer Version 2

SilverlightSerializer version 2

Well, I know it’s been a long wait and I’ve been promising the new version of SilverlightSerializer for months, but it’s finally here, extracted from my core project and working standalone.

This new version of the serializer is a major rewrite – with 2 goals in mind:

1. Performance

Serialization performance is 61% faster (my test harness serializes 100,000 objects in 1.03 seconds compared to 1.72)
Deserialization performance is 27% faster (my test hardness deserializes 100,000 objects in 1.61 seconds compared to 2.04)

These performance gains are on large object graphs containing multiple instances of the same types – it would nominally be slightly slower on small graphs or graphs that contain only one copy of each object – but of course that’s very unlikely in the real world.

2. Extensibility

The new version of SilverlightSerializer abstracts the storage methods behind a new interface, IStorage.  There’s a complete implementation of a BinarySerializer that is compatible with the old version of SilverlightSerializer, but you could add your own.  Alterian has built versions that store data in XML and SQL Server tables.  This means one standard semantic serialization layer can be used with pluggable storage types to put the information where you need it for the application at hand.

That said, writing an IStorage is fairly involved process and I’ll write a post or a page on it if people want me to.

In addition the serializer now handles arrays and enums far better than before and hopefully should avoid the dreaded MissingMethod exception by providing a more developer friendly exception and message while offering the opportunity to construct classes without a parameterless constructor using a new CreateType event that will help some avoid the need to write custom serializers.

You can find the version 2 source code here.  In response to a couple of requests, the project is also accessible on GitHub, see the link at the top of the SilverlightSerializer page.

Why is it faster?

So read on if you’d like to know why it’s faster!

SilverlightSerializer is a reflection based serializer.  It examines an object to identify the available properties and fields. As anyone who is familiar with .NET will know, reflection isn’t exactly the fastest thing in the world.  SS v1 did its level best to cache everything possible, but it still relied on reflection to write and read properties and fields from the underlying objects.  This is why SS didn’t perform as well as the inbuilt serializers in .NET.

Now reflection in this case is a hard thing to get around, I don’t know the types in advance and I have no interest in getting the developer to have to write a custom serialization class for each of their types.

The answer to this performance challenge came from using generic classes and using .NET to construct a new generic class on the fly for each property of each class being serialized.  The generic class accesses the native property access functions directly, rather than have to use reflection.  Fields have to be handled slightly differently, with a reflection function, but at least it’s no slower than the previous method.

First let’s start with the base class that is used for all of the generic classes, it’s called GetSet.


public abstract class GetSet
{
public PropertyInfo Info;
public string Name;
public FieldInfo FieldInfo;
public object Vanilla;
public bool CollectionType;
public abstract object Get(object item);
public abstract void Set(object item, object value);

}

From this base class I create a definition of a generic class, called GetSetGeneric.  This class creates Get and Set delegates using the class parameters, these delegates then map to the functions that will be exposed by the property getters and setters on the target class (neat huh).  This happens only once per type/property combination and the resulting code is the same speed as writing a function to get the value.

public class GetSetGeneric<T, TR> : GetSet
{
public delegate TR GetValue(T obj);
public delegate void SetValue(T obj, TR value);
private readonly GetValue _get;
private readonly SetValue _set;

public GetSetGeneric(PropertyInfo info)
{
MethodInfo getMethod;
MethodInfo setMethod = null;
Name = info.Name;
Info = info;
CollectionType = Info.PropertyType.GetInterface("IEnumerable", true) != null;
getMethod = info.GetGetMethod();
setMethod = info.GetSetMethod();
_get = (GetValue)Delegate.CreateDelegate(typeof(GetValue), getMethod);
if (setMethod != null) _set = (SetValue)Delegate.CreateDelegate(typeof(SetValue), setMethod);
}

public GetSetGeneric(FieldInfo info)
{
MethodInfo getMethod;
MethodInfo setMethod = null;
Name = info.Name;
FieldInfo = info;
_get = new GetValue(GetFieldValue);
_set = new SetValue(SetFieldValue);
CollectionType = FieldInfo.FieldType.GetInterface("IEnumerable", true) != null;
return;
}

public GetSetGeneric(string name)
{
Name = name;
MethodInfo getMethod;
MethodInfo setMethod= null;
var t = typeof(T);
var p = t.GetProperty(name);
if (p == null)
{
FieldInfo = typeof(T).GetField(Name);
_get = new GetValue(GetFieldValue);
_set = new SetValue(SetFieldValue);
CollectionType = FieldInfo.FieldType.GetInterface("IEnumerable", true) != null;
return;
}
Info = p;
CollectionType = Info.PropertyType.GetInterface("IEnumerable", true) != null;
getMethod = p.GetGetMethod();
setMethod = p.GetSetMethod();
_get = (GetValue)Delegate.CreateDelegate(typeof(GetValue), getMethod);
if(setMethod != null) _set = (SetValue)Delegate.CreateDelegate(typeof(SetValue), setMethod);
}

private TR GetFieldValue(T obj)
{
return (TR)FieldInfo.GetValue(obj);
}

private void SetFieldValue(T obj, TR value)
{
FieldInfo.SetValue(obj, value);
}

public override object Get(object item)
{
return _get((T)item);
}

public override void Set(object item, object value)
{
_set((T)item, (TR)value);
}
}

Once that’s done it’s just a matter of creating the generic classes. Deserialization does this one property at a time form the input stream:

entryConfiguration = new EntryConfiguration();

var pi = entry.OwningType.GetProperty(entry.Name);
if (pi != null)
{
entryConfiguration.Type = pi.PropertyType;
var gs = typeof(GetSetGeneric<,>);
var tp = gs.MakeGenericType(new Type[] { entry.OwningType, pi.PropertyType });
entryConfiguration.Setter = (GetSet)Activator.CreateInstance(tp, new object[] { pi });
}
else
{
var fi = entry.OwningType.GetField(entry.Name);
if (fi != null)
{
entryConfiguration.Type = fi.FieldType;
var gs = typeof(GetSetGeneric<,>);
var tp = gs.MakeGenericType(new Type[] { entry.OwningType, fi.FieldType });
entryConfiguration.Setter = (GetSet)Activator.CreateInstance(tp, new object[] { fi });
}
}

Here you can see I get either the property or the field information, use the type of the containing class and the property in a call to MakeGenericType to construct the correct class, then create an instance of that class that can be used to get and set properties on the object.
Serialization works by creating a list of getters and setters for every sensible property on the object, in SilverlightSerializer that creates a few different flavours depending on whether you are using Checksums etc. If you want to see how I do that, have a look at the GetWriteableAttributes class and the GetAccessors() function.

11 Responses

  1. m.

    error in function (when deserializing multidimensional array)
    private static object DeserializeArray(Type itemType, IStorage storage, int count, int objectID)

    result = Array.CreateInstance(elementType, count);
    _loadedObjects[objectID] = result;
    if (count == -1)
    {
    count = storage.BeginReadObjectArray();
    }

    should be

    if (count == -1)
    {
    count = storage.BeginReadObjectArray();
    }
    result = Array.CreateInstance(elementType, count);
    _loadedObjects[objectID] = result;

  2. Scott

    Great job on this. You mentioned that you would write a blog on supporting the IStorage interface because there are some challenges to this. I searched around a little and didn’t see the blog. Are you still planning on blogging about this any time soon? We would like to be able to support the XML serialization to allow human readable/modifiable text to be able to manually set up some test input that we will use with SilverlightSerializer. Thanks

  3. Danny

    Pegasus_cc@163.com SilverlightSerializer Version 2
    Thank you very much.

    I’m Develpoer in china

  4. Alex

    This link doesn’t work from my browser :( : http://whydoidoit.com/silverlight-serializer/

  5. Alex

    Could you generate serialization source code from your graph to have a chance of embedding it in some assembly?
    This way you will have no any reason to deal with reflection during code execution and speed up serialization/deserialization dramatically.
    Would be nice if it could be t4 template with class name to serialize/deserialize and some useful options :)

  6. [...] Read about the updates in this version in Mike Talbot’s Blog [...]

  7. [...] Read about the updates in this version in Mike Talbot's Blog [...]

  8. ray

    Can’t download from china……It.s a tragedy……….

Home Project With Code SilverlightSerializer Version 2