Facebook Twitter Gplus LinkedIn RSS

Silverlight Serializer

Download Download the latest version of Silverlight Serializer v2 (Aug 16th 2011):

Download Download the latest version of Silverlight Serializer v1:

Access the SilverlightSerializer project on GitHub

Overview

SilverlightSerializer is a binary serializer that works in .NET and Silverlight, allowing you to move your objects between the two instances or store and retrieve them locally in either.  It is highly performant and produces small files which are suitable for further compression if you need to.

Silverlight Serializer can serialize and rehydrate just about anything, including complex multi-dimensional arrays of complex objects. By default it can only serialize public fields and properties for classes with a parameterless constructor however you can handle events to construct objects that require parameters for construction and you can write a custom serializer for a particular type; these procedures are detailed below.

The serializer can also produce Checksums that can be used to compare different objects to see if they have the same meaning, or to use as file names to see if you have a cached version already available.

Silverlight Serializer can be used to send information to and from a server perhaps using WCF, to create undo buffers for your app or to save things to the file system on either Silverlight or .NET.  You can also use it to deep copy your objects.

In some basic performance testing on .NET4 it proved comparable to BinaryFormatter when serializing (slightly slower), produced files about 30% of the size of those created by BinaryFormatter and deserializes 60% faster.

Silverlight Serializer is thread safe.

Basic Serialization

The basic functions are achieved by calling:

byte[] myData = SilverlightSerializer.Serialize(anyObject);

and

SomeKindOfObject myObject = SilverlightSerializer.Deserialize(myData) as SomeKindOfObject;

or

SomeKindOfObject myObject = SilverlightSerializer.Deserialize<SomeKindOfObject>(myData);

To create a checksum:

string myChecksum = SilverlightSerializer.GetChecksum(anyObject);

You can also pass streams to the serializer and have it write directly into them, rather than using byte arrays.

SilverlightSerializer.Serialize(anyObject, storageStream);

and

SomeKindOfObject myObject = SilverlightSerializer.Deserialize<SomeKindOfObject>(storageStream);

Object References

If you have object references in your graph, the objects will only be serialized on the first time that they are encountered, therefore the same relationships will return from serialization as existed when the object was persisted.  The serializer keeps a note of the objects that it is persisting and stores references to previously seen items, so everything is the same when you rehydrate the graph.

Advanced Serialization

Silverlight Serializer also enables you to deserialize into an existing instance of an object, which may help in some special circumstances, but bear in mind that it is only to top level properties and fields that will be updated, objects referenced by those fields or properties will be replaced when the instance’s values are overwritten.  I use this so I don’t  have to rehookup references to certain objects, where the limitations mentioned are not an issue.  Use this with care.

SilverlightSerializer.DeserializeInto(myData, existingInstance);

Serialization Attributes

You can control the serialization of properties using the following attributes:

[DoNotSerialize] – the property or field should not be stored ever

[DoNotChecksum] – the property should not be considered when creating a checksum

[CreateUsingEvent] – decorates a class and indicates that you will create instances yourself, by handling the CreateType event (version 2 only)

Deserialization of Past Versions

The latest versions of SilverlightSerializer now handle missing properties on reloading, and can cope with properties changing type if Verbose mode is turned on.  In this mode you can also handle an event MapMissingType that allows you to supply a replacement type if it is semantically similar to a now defunct former type.

SilverlightSerializer.Verbose = true;

Verbose mode is useful if you are saving things to disk and think the objects may change in future.  With Verbose mode turned off, you can use the serializer for undo buffers etc, to talk to a server in real time or to store stable classes (you can add and remove properties, but not change the type of them in the normal mode of operation).

Serializing types that do not have a default constructor (version 2 only)

You have two choices if you want to serialize a type that doesn’t have a parameterless contructor.  You can write a full custom serializer for the class or you can handle the CreateType event that is fired by SilverlightSerializer.  You will need to write a custom serializer if the parameters for the constructor need to be read from storage, if you have the parameters to hand or you can construct an object with default parameters you can go ahead and handle the CreateType event.

If you own the type that needs construction using the CreateType event, you should decorate the class with the [CreateUsingEvent] attribute – this avoids a lot of exception handling in the serializer before the event is fired (it tells it not to try to construct the type itself, which will presumably fail).  The CreateType event will be fired for all instantiation failures, giving you the opportunity to construct system or third party types, but there will be an exception performance penalty as these won’t be decorated with [CreateUsingEvent].  The instance you return from the event must be of the type passed in or a sub class of it.

<br />
SilverlightSerializer.CreateType += new EventHandler&lt;SilverlightSerializer.ObjectMappingEventArgs&gt;(SilverlightSerializer_CreateType);</p>
<p>static void SilverlightSerializer_CreateType(object sender, SilverlightSerializer.ObjectMappingEventArgs e)<br />
{<br />
    if(e.TypeToConstruct == typeof(TestClass)) e.Instance = new TestClass(1);<br />
}<br />

Custom Serialization (all versions)

You can write your own custom serializers for your types and indeed for system types as well.  The way you do this is by creating a class that implements ISerializeObject and decorate it with a SerializerAttribute which declares the type that the item can serialize (one class is allowed to serialize more than one type, by using the attribute more than once).  The methods of ISerializeObject will then be called for your type – this allows you to have objects without a parameterless constructor be serialized in the normal way.  You must register each assembly that contains these decorated extensions by calling SilverlightSerializer.RegisterSerializationAssembly – if you are calling it from the assembly that contains your extension you don’t need to pass a parameter.

If you want to make your serializer store private properties you can implement the interface on the actual item you want to serialize (still decorating it with the attribute) and therefore have access to them.

Here’s a class without a parameterless constructor and containing a complex array:

<br />
public class myClass<br />
{<br />
   public int Parameter;<br />
   public string[,,] Myarray = new string[3,3,3];<br />
   public myClass(int aParameter)<br />
   {<br />
      Parameter = aParameter;<br />
   }<br />
}<br />

Now here’s the definition of a helper class to serialize it:

<br />
[Serializer(typeof(myClass))]<br />
public class SerializeMyClass : ISerializeObject<br />
{<br />
   public object[] Serialize(object target)<br />
   {<br />
        return new object[]<br />
           {<br />
               ((myClass) target).Parameter,<br />
               SilverlightSerializer.Serialize(((myClass)target).Myarray)<br />
            };<br />
}</p>
<p>   public object Deserialize(object[] data)<br />
   {<br />
       var parm = (int) data[0];<br />
       return new myClass(parm)<br />
          {<br />
              Myarray = SilverlightSerializer.Deserialize((byte[]) data[1]) as string[,,]<br />
          };<br />
    }<br />
}<br />

Note you should serialize the complex properties of your class using SilverlightSerializer, don’t waste your time building something complicated to write into the data array if you can help it.Serializing it might look something like this (though you would probably register your serialization extensions during some init phase):

SilverlightSerializer.RegisterSerializationAssembly();<br />
var cls = new myClass(101);<br />
cls.Myarray[0, 1, 1] = &quot;Hello Mum&quot;;<br />
var d = SilverlightSerializer.Serialize(cls);<br />
var x = SilverlightSerializer.Deserialize(d);<br />

Advanced Custom Serialization

You can also use custom serialization in conjunction with the MapMissingType event to enable you to serialize classes between .NET and Silverlight, where some of the classes are not available on one side of the link.

Serializing Classes Between .NET 4 and Silverlight

You may want to use SilverlightSerializer to share objects between Silverlight and .NET 4, perhaps across a WCF link.

The vital thing to do in these circumstances is to define the classes you want to share in a Silverlight assembly that only references System, System.Core and mscorlib.  These are the requirements for assemblies that can be used in both types of project.  If you define your classes in this way then they can be deserialized on the back end without a problem, reference anything else and it won’t work.  Fortunately most of what you will need is included in those system assemblies!

You can add a reference to a compiled DLL of SilverlightSerializer directly to .NET projects – there is no need to compile a separate version.

UPDATE: One of the Alterian team noticed that you can add a binary reference to System.Windows to a .NET4 project, very handy as you can now transfer more types.  You do this by adding the binary DLL file to the .NET 4 project using Browse…

Commentary

I finally got fed up with the terrible performance of the DataContract serializer, especially for shared class libraries and was forced to write my own.

The serializer I have created doesn’t need to know about KnownTypes, is capable of suppressing a property or field with a [DoNotSerialize] attribute and writes to a byte array which is much smaller than the bloated XML format favoured by DataContractSerializer. It also handles object references, which you can’t make DCS do in a shared library.

The serialization won’t waste space and therefore doesn’t serialize properties/fields that have their initial values from the constructor. It won’t bother serializing empty collections etc and it uses tokens for type names and property names to save space; though I’m also using SharpZip to compress the output further in a wrapper I’ve put around the serializer in my own code (not shown here, or in the example project). This is sometimes necessary as it does store the AssemblyQualifiedName of the types in the graph (which is the trade off for not marking everything with KnownType in advance). I might look at a way of adding that functionality myself later, but my version doesn’t have problems with conflicting names that DCS frequently pops up.

So you can find the project here if you are interested, feel free to use or modify it in any way you like.

Here’s an example of using it:

<br />
        public class outer<br />
        {<br />
            public enum MyModes<br />
            {<br />
                One,<br />
                Two,<br />
                Three<br />
            };<br />
            public string Name { get; set; }<br />
            public Dictionary Dict { get; set; }<br />
            public BaseItem[] Array { get; set; }<br />
            public MyModes Mode { get; set; }<br />
            public outer()<br />
            {<br />
                Name = string.Empty;<br />
                Dict = new Dictionary();<br />
                Array = new BaseItem[10];<br />
            }<br />
        }<br />

Calling the serializer:

<br />
             var s = new outer();<br />
            var br = new Branch();  //One of my complex objects<br />
            br.Elements.Add(new FilterBranch());<br />
            s.Dict[&quot;Hello&quot;] = br;<br />
            s.Array[2] = new Campaign();<br />
            s.Mode = outer.MyModes.Two;</p>
<p>            var bytes = SilverlightSerializer.Serialize(s);<br />
            var item = SilverlightSerializer.Deserialize(bytes)  as outer;<br />

Silverlight Serializer In Action

Peter Bromberg has come up with a great article on EggheadCafe that combines my serializer with a compression library and some utility classes plus examples. Check it out here.

The serializer has been linked into a persistent storage library for client side isolated storage by Ashiq Alibhai that you can find here.

Home Forums Silverlight Serializer

This topic contains 6 replies, has 4 voices, and was last updated by  highdown 1 year ago.

Viewing 7 posts - 1 through 7 (of 7 total)
  • Author
    Posts
  • #1302

    whydoidoit
    Keymaster

     Download the latest version of Silverlight Serializer v2 (Aug 16th 2011): Download the latest version of Silverlight Serializer v1: Access the Silver
    [See the full post at: Silverlight Serializer]

    #1450

    carl.herbert
    Participant

    Your serializer work fantastic! I have one problem, how can I get it to serialize an ObservableCollection?

    I read on another blog you can convert it to a List<T>.

    How do i setup the class to do this for all ObservableCollections… some sort of extension method perhaps?

    #2004

    evgeny
    Participant

    Here is a problem I came across when using the Serializer:

    public enum Test    {Zero, One}

    Test[] testArray = new Test[]{Test.One, Test.Zero};

    byte[] serializedTest = SilverlightSerializer.Serialize(testArray);

    Test[] deserializedArray = (Test[])SilverlightSerializer.Deserialize(serializedTest);

    I get an InvalidCastException “Object cannot be stored in an array of this type.” in public Array ReadSimpleArray(Type elementType, int count), line result.SetValue(this.ReadSimpleValue(elementType), l);

    If it looks like a valid bug, I could attempt fixing it – guidance and advice welcome!

    Part of Stack Trace:

    ============================

    at System.Array.InternalSetValue(Void* target, Object value)

    at System.Array.SetValue(Object value, Int32 index)

    at Serialization.BinarySerializer.ReadSimpleArray(Type elementType, Int32 count) in C:\Evgeny\Performance\SilverlightSerializer\SilverlightSerializer_v2\Serialization.SL\SerializationUnits.cs:line 454

    at Serialization.SilverlightSerializer.DeserializeArray(Type itemType, IStorage storage, Int32 count, Int32 objectID) in C:\Evgeny\Performance\SilverlightSerializer\SilverlightSerializer_v2\Serialization.SL\SilverlightSerializer.cs:line 1123

    at Serialization.SilverlightSerializer.DeserializeObject(Entry entry, IStorage storage) in C:\Evgeny\Performance\SilverlightSerializer\SilverlightSerializer_v2\Serialization.SL\SilverlightSerializer.cs:line 1066

    at Serialization.SilverlightSerializer.Deserialize(Stream inputStream, Object instance) in C:\Evgeny\Performance\SilverlightSerializer\SilverlightSerializer_v2\Serialization.SL\SilverlightSerializer.cs:line 499

    at Serialization.SilverlightSerializer.Deserialize(Byte[] bytes) in C:\Evgeny\Performance\SilverlightSerializer\SilverlightSerializer_v2\Serialization.SL\SilverlightSerializer.cs:line 562

    =================================

    Evgeny

    #2049

    evgeny
    Participant

    I submitted my attempt at solving the problem described in the post above on GitHub.

    In the meantime I found out that the following test will generate an “Object reference not set to an instance of an object”

    public void Serializes_Deserializes_Empty_Array()
    {
    string[] emptyArray = new string[]{null};

    byte[] serializedEmptyArray = SilverlightSerializer.Serialize(emptyArray);
    string[] deserializedEmptyArray = (string[])SilverlightSerializer.Deserialize(serializedEmptyArray);

    CollectionAssert.AreEqual(emptyArray, deserializedEmptyArray);
    }

    Evgeny

    #2051

    evgeny
    Participant

    Sorry for spamming this thread with huge walls of code. The following unit test will also fail, I’ll have to investigate this one too …

    public class TestSerializationObject
    {
    public User[,] MdArrayProperty { get; set; }
    }

    public class User
    {
    public string FirstName { get; set; }
    }

    [TestMethod]
    public void Serializes_Deserializes_MultiDimensionArray()
    {
    var testObject = new TestSerializationObject
    {
    MdArrayProperty = new[,]
    {
    { new User { FirstName = “aaa” } }
    }
    };

    string[,] testArray = new[,] {{“1″, “2″}, {“3″, “4″}};
    byte[] serializedTestArray = SilverlightSerializer.Serialize(testArray);
    string[,] deserializedTestArray = (string[,]) SilverlightSerializer.Deserialize(serializedTestArray);

    Assert.AreEqual(deserializedTestArray[0, 0], “1″);
    Assert.AreEqual(deserializedTestArray[0, 1], “2″);
    Assert.AreEqual(deserializedTestArray[1, 0], “3″);
    Assert.AreEqual(deserializedTestArray[1, 1], “4″); //perfectly fine up to here

    byte[] serializedTestObject = SilverlightSerializer.Serialize(testObject);
    //fails here – tries to call Array.CreateInstance(elementType, count) with count = -1 and gets “Non-negative number required.”
    TestSerializationObject deserializedTestObject = (TestSerializationObject)SilverlightSerializer.Deserialize(serializedTestObject);

    Assert.AreEqual(testObject.MdArrayProperty.Length, deserializedTestObject.MdArrayProperty.Length);
    Assert.AreEqual(testObject.MdArrayProperty[0, 0].FirstName, deserializedTestObject.MdArrayProperty[0, 0].FirstName);
    }

    Evgeny

    #2053

    evgeny
    Participant

    I hereby humbly suggest moving two lines of code in the DeserializeArray(Type itemType, IStorage storage, int count, int objectID) as follows:

    Before:


    else
    {
    result = Array.CreateInstance(elementType, count);
    _loadedObjects[objectID] = result;

    if (count == -1)
    {
    count = storage.BeginReadObjectArray();
    }

    for (var l = 0; l < count; l++)
    {

    After:


    else
    {
    if (count == -1)
    {
    count = storage.BeginReadObjectArray();
    }

    result = Array.CreateInstance(elementType, count);
    _loadedObjects[objectID] = result;

    for (var l = 0; l < count; l++)
    {

    Otherwise, when DeserializeArray is called from DeserializeArrayPart with -1 as a parameter, and the object is not a simple type, bad things start happening …

    Evgeny

    #2540

    highdown
    Participant

    Hello Mike,

    I have used your .NET/Silverlight serializer for well over a year with great success. It has proven to be a lot better (speed/size) than some of the other options that were available.

    However, I now have need to share serialized data (via Windows Server) between HTML5 applications and Silverlight clients running on iOS, Android, and Windows. I need to compact/serialize data on the clients, then ship to server to disperse to any/all of the cross platform clients.

    Do you know of any readily available technology/approach to accomplish this?

    Thanks…

Viewing 7 posts - 1 through 7 (of 7 total)

You must be logged in to reply to this topic.

Home Silverlight Serializer