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 the 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 a 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 those 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 a 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:
- Writing a custom serializer (Custom Serializer Pattern)
- Creating a substitute with the default constructor (Substitute Pattern)
Further, I'll explain which one is better and why.
Let's assume there is the following object to be serialized:
public class TypeWithoutStandardConstructor
{
public TypeWithoutStandardConstructor(object data){}
}
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
{
void Serialize(Stream dest, object obj){}
object Deserialize(Stream source){}
}
Below, there's a sample implementation:
[SerializedType(typeof (TypeWithoutStandardConstructor)]
public class MyCustomSerializer : ICustomSerializer
{
public void Serialize(Stream dest, object data)
{
}
public object Deserialize(Stream source)
{
}
}
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 a custom serializer:
- No need to modify serialized classes.
Disadvantages:
- Searching for an 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 a 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 it is not the TypeWithoutStandardConstructor
that 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
{
public Substitute(){}
public static Substitute CreateSubstitute(
TypeWithoutStandardConstructor original)
{
}
public TypeWithoutStandardConstructor CreateOriginal()
{
}
public property VitalProperty1 { get; set; }
}
The substitute class contains all the 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 substitute's
CreateOriginal()
function. The CreateOriginal()
function is some kind of object factory.
Advantages of the 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 the original object and its substitute must be made prior to 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. The 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 the complexity of the Custom Serializer Pattern.
The second one is its flexibility. Each serialization engine I know supports serialization of classes with a 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, .NET Compact, and Silverlight.
It can serialize data into XML and binary format. sharpSerializer uses the Substitute Pattern for serialization.
The interesting part of System.Windows.Media.FontFamily
is shown below:
public class FontFamily
{
public FontFamily(string familyName)
{
Source = familyName;
}
public string Source { get; set; }
}
An equivalent substitute class is created:
public class FontFamilySubstitute
{
public FontFamilySubstitute()
{
}
public string FamilyName { get; set; }
public static FontFamilySubstitute CreateSubstitute(FontFamily fontFamily)
{
var substitute = new FontFamilySubstitute();
substitute.FamilyName = fontFamily.Source;
return substitute;
}
public FontFamily CreateFontFamily()
{
return new FontFamily(FamilyName);
}
}
The 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))
{
var sharpSerializer = new SharpSerializer();
sharpSerializer.Serialize(data, file);
}
}
}
public static T Deserialize<T>(string filename)
{
using (IsolatedStorageFile appStorage =
IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream file =
appStorage.OpenFile(filename, FileMode.Open))
{
var sharpSerializer = new SharpSerializer();
return (T) sharpSerializer.Deserialize(file);
}
}
}
}
The last step is writing the serialization flow.
var fontFamily1 = new FontFamily("Times New Roman");
var substitute1 = FontFamilySubstitute.CreateSubstitute(fontFamily1);
IsolatedStorageSerializer.Serialize(substitute1, "font.xml");
var substitute2 =
IsolatedStorageSerializer.Deserialize<FontFamilySubstitute>("font.xml");
var fontFamily2 = substitute2.CreateFontFamily();
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 a Silverlight app on WP7 and the full .NET service, then sharpSerializer will not disappoint you.
As a user of the NuGet.org plug-in 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 CodeProject:
If you like this article, please rate it with 5. If not, please make a comment below ;-)
History
- August 14, 2011 - First release.