Introduction
A Silverlight application has by default 1MB of available space in the isolated storage to store its local data. If started as an "out of browser" window, this amount increases to 25MB, even if the application has no elevated permissions. You can check the free memory amount by pressing the right mouse button on your Silverlight program, then go to the registry "Application storage" and there you see it.
Unfortunately sometimes there is more storage needed. For example - a briefcase model - a sales agent downloads data from the server, works offline with the data by a customer, and finally synchronizes the data with the server.
You can always demand more free space for the application using the method IncreaseQuotaTo()
of the IsolatedStorageFile
class, as described in the following article:
However increasing only the storage size is not optimal. A better solution would be saving the data more efficiently than to XML.
Background
DataContractSerializer
is a standard way of serializing business objects by a Silverlight client. But it serializes to XML which is known as a wasteful way of storing data. A better choice would be a binary serialization. But there is a problem - there is no BinaryFormatter
in Silverlight at all. :-(
A third-party software is needed. Let's take for example sharpSerializer - an open source XML and binary serializer for .NET, .NET Compact and Silverlight - and compare it with BinaryFormatter
from the full .NET Framework.
BinaryFormatter vs. sharpSerializer
As I said before - there is no BinaryFormatter
in Silverlight. To get some results anyway, I checked the serialization speed and output file size of BinaryFormatter
from the full version of .NET Framework and compared these values with the results of sharpSerializer
v.2.6.
Serialized was an array of 10.000 elements of type RootContainer - a very sophisticated sample class from the HelloWorldDemo of sharpSerializer
. I had to mark the RootContainer
and all of its subclasses with [Serializable]
as it's demanded by BinaryFormatter
to work. Btw, SharpSerializer
needs no additional attributes in serialized objects, nor any type definitions in its Serialize()
method, as i.e. XmlSerializer
needs.
Below you see the main body of the RootContainer
. To see all of its subclasses, refer to the source code of sharpSerializer (There is another article describing serialization of weird classes like the RootContainer
to XML - XML Serialization of Generic Dictionary, Multidimensional Array, and Inherited Type, with sharpSerializer .NET).
[Serializable]
public class RootContainer
{
public SByte SimpleSByte { get; set; }
public int SimpleInt { get; set; }
public Single SimpleSingle { get; set; }
public double SimpleDouble { get; set; }
public DateTime SimpleDateTime { get; set; }
public TimeSpan SimpleTimeSpan { get; set; }
public SimpleEnum SimpleEnum { get; set; }
public FlagEnum FlagsEnum { get; set; }
public decimal SimpleDecimal { get; set; }
public string SimpleString { get; set; }
public string EmptyString { get; set; }
public AdvancedStruct AdvancedStruct { get; set; }
public string[] SingleArray { get; set; }
public string[,] DoubleArray { get; set; }
public IComplexObject[] PolymorphicSingleArray { get; set; }
public IList<string> GenericList { get; set; }
public IDictionary<int, string> GenericDictionary { get; set; }
public IDictionary<int,
IComplexObject> GenericDictionaryOfPolymorphicValues { get; set; }
public IComplexObject ComplexObject { get; set; }
public ComplexObjectPolymorphicCollection ComplexObjectCollection { get; set; }
public ComplexObjectPolymorphicDictionary ComplexObjectDictionary { get; set; }
public IList<IComplexObject> GenericListOfComplexObjects { get; set; }
public GenericObject<IComplexObject> GenericObjectOfComplexObject { get; set; }
public GenericObject<IComplexObject>[,]
MultiArrayOfGenericObjectWithPolymorphicArgument { get; set; }
public object[] SingleArrayOfObjects { get; set; }
}
For testing purposes, I made a small benchmark console program to measure the serialization and deserialization time and to store the serialization results as files - to compare their sizes.
The "Main
" method produces 10.000 elements of the RootContainer
and invokes single test methods:
static void Main(string[] args)
{
RootContainer[] containerArray = createContainerArray(10000);
Console.WriteLine("Serializing");
serializeWithBinaryFormatter(containerArray, "BinaryFormatter.bin");
serializeWithSharpSerializer(containerArray,
BinarySerializationMode.Burst, "sharpSerializerBurst.bin");
serializeWithSharpSerializer(containerArray,
BinarySerializationMode.SizeOptimized, "sharpSerializerOptimized.bin");
serializeWithXmlSharpSerializer(containerArray, "sharpSerializer.xml");
Console.WriteLine();
Console.WriteLine("Deserializing");
deserializeWithBinaryFormatter("BinaryFormatter.bin");
deserializeWithSharpSerializer(BinarySerializationMode.Burst,
"sharpSerializerBurst.bin");
deserializeWithSharpSerializer(BinarySerializationMode.SizeOptimized,
"sharpSerializerOptimized.bin");
deserializeWithXmlSharpSerializer("sharpSerializer.xml");
}
The following method uses BinaryFormatter
for the serialization. Note! Stopwatch
is the most adequate class in .NET to measure time.
private static void serializeWithBinaryFormatter
(RootContainer[] containerArray, string shortFilename)
{
string filename = getFilename(shortFilename);
using (var stream = new FileStream(filename, FileMode.Create))
{
var formatter = new BinaryFormatter();
Stopwatch watch = new Stopwatch();
Console.WriteLine("Starting serialization with BinaryFormatter");
watch.Start();
formatter.Serialize(stream, containerArray);
watch.Stop();
Console.WriteLine(string.Format
("Stopped after {0}ms", watch.ElapsedMilliseconds));
}
}
To serialize the test object with sharpSerializer
, I created the following method. Btw, SharpSerializer
has much easier syntax than BinaryFormatter
.
private static void serializeWithSharpSerializer
(RootContainer[] containerArray, BinarySerializationMode mode, string shortFilename)
{
string filename = getFilename(shortFilename);
var serializer = new SharpSerializer(mode)
Stopwatch watch = new Stopwatch();
Console.WriteLine(string.Format("Starting serialization with SharpSerializer ({0})",
Enum.GetName(typeof(BinarySerializationMode), mode)));
watch.Start();
serializer.Serialize(containerArray, filename);
watch.Stop();
Console.WriteLine(string.Format("Stopped after {0}ms", watch.ElapsedMilliseconds));
}
Serialization and deserialization times on the program screen:
Serialization and deserialization results in a table (For serializing of other objects, the results and their proportions can vary):
Serializer |
Serialization time [s] |
File size [MB] |
Deserialization time [s] |
.NET BinaryFormatter |
2.1 |
19.3 |
7.9 |
sharpSerializer - binary (Burst) |
3.1 |
3.8 |
3.7 |
sharpSerializer - binary (SizeOptimized) |
3.8 |
8.5 |
2.6 |
sharpSerializer - XML |
3.7 |
86.0 |
17.6 |
The fastest serialization of 2.1s offers BinaryFormatter
, but its file size of 19.3MB is twice as big as produces sharpSerializer
in its binary SizeOptimized
mode (you can find more about binary modes of sharpSerializer
in the tutorial on the project page).
The deserialization time of BinaryFormatter
is over 3 times longer than the best time 2.6s of sharpSerializer
.
Result Analysis
Server Side
Where the full .NET Framework is available, you have a choice between BinaryFormatter
and sharpSerializer
to make a binary serialization. If you write data only once and read it multiple times (i.e. photo gallery or an mp3 music store) - then sharpSerializer
is a better choice than the built-in BinaryFormatter
. It stores 80% slower but reads 300% faster then BinaryFormatter
. Not to mention the file size which is 50% smaller if serialized with sharpSerializer
.
Silverlight Client Side
In Silverlight, there is no choice if you want to use binary serialization. As there is no BinaryFormatter
in Silverlight, the only solution is sharpSerializer
.
Advantages of sharpSerializer
- Very easy syntax
- No need to mark the serialized object with additional attributes
- No need to declare serialized types in the
Serialize()
method
- Small size of the produced file
- Fast deserialization of objects
- Common algorithm and serialization compatibility between .NET Full, .NET Compact and Silverlight
- Open Source and free for any purpose
Disadvantages of sharpSerializer
- Only
public
properties are serialized
- Only objects with standard constructor are deserialized
- XML and binary format is not compatible with other .NET standards
Conclusion
Among many advantages and good benchmarks, there are also some disadvantages of sharpSerializer
. It depends on the context, if the disadvantages are a stone in the shoe.
As I use sharpSerializer
mostly to store my own business objects, there is no problem for me to provide a standard constructor for every object. There is also no problem in using only public
properties to store the object's state.
If there are complicated third-party classes to serialize, they should be converted with an adapter pattern into simple ones. The adapter pattern simplifies the output and decreases its size in most cases, as only needed properties are serialized.
sharpSerializer
is no more Beta, its current version is 2.6. It works stable and reliable.
One of the future milestones of sharpSerializer
is data compression. Actually there are problems with finding an open source substitute for DeflateStream
for Silverlight as GZipStream
and DeflateStream
are not supported in Silverlight 4 and earlier versions. After the data compression is implemented, the list with advantages of sharpSerializer
can be extended with a point "very, very small size of the produced file". :-)
From the Author
There is another article about XML serialization with sharpSerializer:
Of course, you are encouraged to make your own tests with sharpSerializer Framework. If you like this article, please rate it, if not - tell me why in the comment field below. ;-)
History
- October 6, 2010 - First release