Introduction

XmlSerializer, SharpSerializer and many other serialization engines cannot deserialize types which have no default constructor. In this article I'll tell you how to effectively serialize difficult objects which have no default constructor. I'll also explain the differences between Custom Serializer Pattern and Substitute Pattern and tell you which one is better and why. At last I'll show you an example of serialization of an object without default constructor into isolated storage in WP7 with SharpSerializer, an open source serializer for .NET Full, .NET Compact and Silverlight.

Background

Every mama says: Serializer is not a memory dumper. It should not persist every single byte from the application. During serialization only vital data should be stored. There is no need to serialize all the properties and fields of an object. Only these object members should be stored which are necessary to restore the object state. Other members should be ignored during serialization. They can be restored later from the vital ones. This strategy has two big advantages:

  • serialized data has smaller size,
  • serialization is faster.

How to serialize Bitmap, Cursor, Icon, Font and other types without default constructor?

The simplest answer - Don't! These types are complicated, have internal handles, streams, references and should not be serialized in the whole.

Custom serialization of these types can be done in many ways. Below are two of them presented:

  1. Writing a custom serializer (Custom Serializer Pattern)
  2. Creating a substitute with the default constructor (Substitute Pattern)

Further I'll explain which one is better and why.

Let's assume, there is a following object to be serialized:

public class TypeWithoutStandardConstructor
{
    // Constructor with parameters
    public TypeWithoutStandardConstructor(object data){}

    // Some important members
    // ...
    // Some unimportant members
    // ...
}

Pattern 1 - Serializing with a custom serializer

Some third party serialization engines contain a placeholder for custom serializers. Custom serializers are defined and added to this placeholder (i.e. Dictionary<Type, ICustomSerializer>) before serialization begins. During serialization, the serialization engine investigates serialized members. If a serialized member is of a special type, the dedicated custom serializer will be used to serialize this member. If not, the default serialization engine serializes this member.

A custom serializer can serialize the object to a stream and deserialize it from another stream. Its interface could be like this:

public interface ICustomSerializer
{
    // Stores object to the stream
    void Serialize(Stream dest, object obj){}

    // Creates object from the stream
    object Deserialize(Stream source){}
}

Below there's a sample implementation:

[SerializedType(typeof (TypeWithoutStandardConstructor)]
public class MyCustomSerializer : ICustomSerializer
{
    public void Serialize(Stream dest, object data)
    {
        // Serialize important members and ignore unimportant
    }

    public object Deserialize(Stream source)
    {
        // Create the object instance
        // deserialize important members
        // and calculate unimportant ones
    }
}

The SerializedTypeAttribute is a bridge between the serialized type and its serializer. Custom serializers are stored in a dictionary before the serialization begins.

private Dictionary<Type, ICustomSerializer> _customSerializerMappings = new Dictionary<Type, ICustomSerializer>();

During serialization the serialization engine investigates every serialized type if there is an appropriate custom serializer defined. If there is one, the member of this type is serialized by the custom serializer. If no custom serializer is found, the member is serialized by the serialization engine itself. How the serialization engine works is out of the scope of this article. For more information please refer to the SharpSerializer project page and search for the chapter "How does sharpSerializer work?".

Advantages of using custom serializer:

  • No need to modify serialized classes.

Disadvantages:

  • Searching for adequate ICustomSerializer for every serialized type slows the serialization.
  • Low level serialization must be made (custom serializers serialize directly to the stream). Each serialization format (XML, binary, JSON etc.) needs dedicated serializer.
  • Different serialization engines need dedicated custom serializers. There is no single ultimate ICustomSerializer interface.
  • Custom Serializers must be additionally written by a programmer. 

Pattern 2 - Serializing with a substitute class

In the Substitute Pattern not the TypeWithoutStandardConstructor is serialized but its substitute which has the default constructor.

SharpSerializer uses this pattern. All vital properties are copied from the original object to the substitute. Fields of the original object are converted in public properties of the substitute (for performance reasons SharpSerializer serializes only public properties)

public class Substitute
{
    // default constructor is required for serialization
    public Substitute(){}

    // creates substitute from the original
    public static Substitute CreateSubstitute(TypeWithoutStandardConstructor original)
    {
        // Create substitute instance,
        // fill its properties from the original object,
        // convert fields of the original object in public properties of the substitute
    }

    public TypeWithoutStandardConstructor CreateOriginal()
    {
        // create original instance, even without default constructor,
        // copy all important members from the substitute,
        // calculate unimportent ones
    }

    // Only important properties and fields of the original
    public property VitalProperty1 { get; set; }
    // ...
}

The substitute class contains all vital members of the object. They are copied from the original object in the Substitute.CreateSubstitute() static function. Then the substitute is serialized. During deserialization the substitute is reloaded. The original object is created in the substitutes CreateOriginal() function. The CreateOriginal() function is some kind of object factory.

Advantages of substitute pattern:

  • No need to alter original objects.
  • No time waste for searching the list with custom serializers.
  • No low level serialization must be made. Stream reading/writing is made by the serialization engine.
  • No need to adapt the code if working with different serialization engines.

Disadvantages:

  • Substitute must also be written by the programmer.
  • Conversion between original object and its substitute must be made prior serialization and after deserialization.

Which one is better? Custom Serializer or Substitute Pattern?

In both patterns additional code must be written. Either it's a custom serializer or a substitute class. Original object must be modified in neither of them. Creating substitutes slows the serialization but querying the dictionary with the custom serializers for each serialized type can be a bigger time penalty.

The first certain advantage of the Substitute Pattern is leaving the low level serialization to the serialization engine. The necessity of making a different custom serializer for every serialization format (XML, binary, JSON) multiplies complexity of the Custom Serializer Pattern.

The second one is its flexibility. Each serialization engine I know, supports serialization of classes with default constructor and public properties. Therefore a migration from one serialization engine to another is no problem at all.

In my opinion Substitute Pattern clearly wins this competition.

Example of serialization of FontFamily to IsolatedStorage in WP7 using the Substitute Pattern and SharpSerializer

System.Windows.Media.FontFamily is a part of the Silverlight library. It does not have the default constructor and must be serialized in a custom way.

SharpSerializer is an open source serializer for .NET Full, .NET Compact and Silverlight. It can serialize data into XML and binary format. SharpSerializer uses the Substitute Pattern for the serialization.

The interesting part of System.Windows.Media.FontFamily is below:

public class FontFamily
{
    public FontFamily(string familyName)
    {
        Source = familyName;
    }

    public string Source { get; set; }
}

An equivalent substitute class is created

public class FontFamilySubstitute
{
    // default constructor is required
    // for the serialization
    public FontFamilySubstitute()
    {
    }

    // vital property, it will be serialized
    public string FamilyName { get; set; }

    // creates substitute from the FontFamily
    public static FontFamilySubstitute CreateSubstitute(FontFamily fontFamily)
    {
        var substitute = new FontFamilySubstitute();

        // Copy properties from the original object
        substitute.FamilyName = fontFamily.Source;

        return substitute;
    }

    // creates FontFamily from the substitute
    public FontFamily CreateFontFamily()
    {
        // creating the object instance
        // and initializing it
        return new FontFamily(FamilyName);
    }
}

Helper class simplifies serializing to IsolatedStorage in WP7.

public static class IsolatedStorageSerializer
{
    public static void Serialize(object data, string filename)
    {
        using (IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication())
        {
            using (IsolatedStorageFileStream file = appStorage.OpenFile(filename, FileMode.Create))
            {
                // creating serializer
                var sharpSerializer = new SharpSerializer();

                // serialize data
                sharpSerializer.Serialize(data, file);
            }
        }
    }


    public static T Deserialize<T>(string filename)
    {
        using (IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication())
        {
            using (IsolatedStorageFileStream file = appStorage.OpenFile(filename, FileMode.Open))
            {
                // creating serializer
                var sharpSerializer = new SharpSerializer();

                // deserialize data
                return (T) sharpSerializer.Deserialize(file);
            }
        }
    }
}

The last step is writing the serialization flow.

// Creating FontFamily
var fontFamily1 = new FontFamily("Times New Roman");

// Creating Substitute
var substitute1 = FontFamilySubstitute.CreateSubstitute(fontFamily1);

// Serializing to isolated storage
IsolatedStorageSerializer.Serialize(substitute1, "font.xml");

// Deserializing from isolated storage
var substitute2 = IsolatedStorageSerializer.Deserialize<FontFamilySubstitute>("font.xml");

// Converting to FontFamily
var fontFamily2 = substitute2.CreateFontFamily();

// Comparing
System.Diagnostics.Debug.Assert(fontFamily1==fontFamily2);

Some words about SharpSerializer

If you are a WP7 programmer, or even not;-), and you need an easy way for storing your app settings, or if you just need to quickly serialize your business objects between the Silverlight app on WP7 and the full .NET service, then SharpSerializer will not disappoint you.

As a user of the NuGet.org plugin for VS, just search for "sharpserializer" after clicking the context item "Add library package reference.." in your solution explorer.

There are other articles about SharpSerializer on the Code Project:

If you like this article, please rate it with 5. If not, please make a comment below ;-)

History

  • August 14, 2011 - First release
推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架
新浪微博粉丝精灵,刷粉丝、刷评论、刷转发、企业商家微博营销必备工具"