SharpSerializer
You may use sharpSerializer free of charge for any purpose.

Though sharpSerializer is no more Beta, is reliable and stable.., it's still young, it's a baby.

Don't leave the baby alone!

To grow a healthy baby, don't ask - how to donate ;-)

Instead just place a link to sharpSerializer or recommend it to your friends.

Let's grow the baby together!

Pawel Idzikowski
Author of sharpSerializer
New - sharpSerializer on CodePlex
sharpSerializer is now on GitHub!

Tutorial

Below are some programming examples of sharpSerializer. If you'd like to see the XML code, which the HelloWorldDemo produces, take a look at the Example of  XML serialization with sharpSerializer.

There are also two articles on Code Project concerning sharpSerializer:


There you find more examples of Xml and binary serialization with sharpSerializer.

Examples

Hello world with xml serialization

The easiest way to use sharpSerializer is to instantiate it with its standard constructor. Out of the box sharpSerializer serializes to Xml.


// create fake obj
var obj = createFakeObject();

// create instance of sharpSerializer
// with standard constructor it serializes to xml
var serializer = new SharpSerializer();

// serialize
serializer.Serialize(obj, "test.xml");

// deserialize
var obj2 = serializer.Deserialize("test.xml");

Hello world with binary serialization

To activate binary serialization you need to use overloaded constructor.


// create fake obj
var obj = createFakeObject();

// create instance of sharpSerializer
// true - binary serialization, false - xml serialization
var serializer = new SharpSerializer(true);

// serialize
serializer.Serialize(obj, "test.bin");

// deserialize
var obj2 = serializer.Deserialize("test.bin");

Different modes of the binary serialization

There are two modes of binary serialization: SizeOptimized and Burst.

What is the difference?
To successfully restore an object tree from the serialized stream, all objects must be serialized including their type information. Both modes differ in the art the type information is stored.

BinarySerializationMode.Burst
In the burst mode, type of every object is serialized as part of this object. It doesn't matter if all serialized objects are of the same type, their types are  serialized as many times, as many objects. Type information is duplicated. It increases the file size especially when serializing listings of items of the same type (collections, arrays, dictionaries). This mode is recommended only for serializing of single, simple objects. It has small overhead and no extended logic. BurstBinaryWriter supports this mode.

// create sharpSerializer in the burst binary mode
// overloaded constructor of SharpSerializerBinarySettings accepts one value from the enumeration BinarySerializationMode
var settings = new SharpSerializerBinarySettings(BinarySerializationMode.Burst);
var burstSerializer = new SharpSerializer(settings);



BinarySerializationMode.SizeOptimized (the default one)
In the SizeOptimized mode all types are gathered in a list which is stored in the file header. All type duplicates are removed. Serialized objects refer only to this list using index of the correlated type. It's recommended approach for serializing of complex objects with many properties, or serializing of listings. The drawback is - serialized objects must be at first analysed, then their types are cached in the list, duplicates are removed, type indexes are estimated and injected back to the objects. Finally the data is written to stream.
Apart from types, the same optimization is applied to property names. SizeOptimizedBinaryWriter supports this mode.

// create sharpSerializer in the size optimized binary mode - Default
// overloaded constructor accepts bool value. If true then binary serialization, if false - xml
var sizeOptimizedSerializer1 = new SharpSerializer(true);

// or with the same usage as for the burst mode
var settings = new SharpSerializerBinarySettings(BinarySerializationMode.SizeOptimized);
var sizeOptimizedSerializer2 = new SharpSerializer(settings);

// serialize

(...)

Serializing type as AssemblyQualifiedName or as a short type name

Since SharpSerializer v.2.12 are all types serialized as AssemblyQualifiedName. Earlier was used short type name "TypeName, AssemblyName", i.e.:
type="System.String, mscorlib"

This was simple to read, output size was small. But it encoutered problems during deserialization if working with signed assemblies or their specific versions. If you work with an earlier version as 2.12 of SharpSerializer you need to explicitly activate serializing to the full Type.AssemblyQualifiedName.

You reach the goal altering settings of sharpSerializer.

// create the settings
var settings = new SharpSerializerBinarySettings(); // for binary mode
var settings = new SharpSerializerXmlSettings(); // for xml mode

// configure the type serialization
settings.IncludeAssemblyVersionInTypeName = true;
settings.IncludeCultureInTypeName = true;
settings.IncludePublicKeyTokenInTypeName = true;

// instatiate sharpSerializer
var serializer = new SharpSerializer(settings);

It results with the following type name:
type="System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"

Custom formatting of DateTime and float values in Xml, changing culture

In xml serialization are all primitive types like float numbers and DateTime values converted to string according to CultureInfo.InvariantCulture. The string format can be changed by overriding the culture.

// change culture
var settings = new SharpSerializerXmlSettings();
settings.Culture = System.Globalization.CultureInfo.CurrentCulture;

// create serializer
var serializer = new SharpSerializer(settings);

Changing Encoding for strings

Encoding in binary serialization affects storage format of all strings (type names, property names, values of text properties). Encoding in Xml serialization applies to the whole xml file. Out of the box is UTF-8 selected. If no international encoding is needed, you can override the default encoding to decrease the file size.

// create the settings
var settings = new SharpSerializerBinarySettings(); // for binary mode
var settings = new SharpSerializerXmlSettings(); // for xml mode

// change encoding
settings.Encoding = System.Text.Encoding.ASCII;

// create serializer
var serializer = new SharpSerializer(settings);

Changing name of the root element

There is always one root element during serialization. Its default name is "Root", but it can be changed as follows.

// create the settings
var settings = new SharpSerializerBinarySettings(); // for binary mode
var settings = new SharpSerializerXmlSettings(); // for xml mode

// change RootName
settings.AdvancedSettings.RootName = "MyRootElement";

Using custom SimpleValueConverter in Xml serialization

SimpleValueConverter is used during Xml serialization to converting values of simple types (all primitive types, DateTime, TimeSpan and decimal) to string and vice versa. Float values and DateTime are by default converted to format of the settings.Culture or CultureInfo.InvariantCulture if the settings.Culture is not set.

Changing the default SimpleValueConverter is possible to store these values in other format (Morse alphabet?). Custom converter must implement ISimpleValueConverter.

// create the settings (this setting applies only to xml)
var settings = new SharpSerializerXmlSettings();

// change SimpleValueConverter
ISimpleValueConverter converter = new MyMorseAlphabetSimpleValueConverter();
settings.AdvancedSettings.SimpleValueConverter = converter;

Important! This setting overrides the settings.Culture

Using custom TypeNameConverter

TypeNameConverter converts type to type name as string and vice versa. Since SharpSerializer 2.12 all types are serialized as AssemblyQualifiedName. Former all types were serialized as <typeName, assemblyName>. There was no consideration about Version, Culture and PublicKeyToken. To influence serializing of type names you alter the IncludeAssemblyVersionInTypeNameIncludeCultureInTypeName and IncludePublicKeyTokenInTypeName properties, or create your own instance of ITypeNameConverter.

// create the settings
var settings = new SharpSerializerBinarySettings(); // for binary mode
var settings = new SharpSerializerXmlSettings(); // for xml mode

// change TypeNameConverter
settings.AdvancedSettings.TypeNameConverter = new MyTypeNameConverterWithCompressedTypeNames();

Important! This property overrides the three properties IncludeAssemblyVersionInTypeName, IncludeCultureInTypeName and IncludePublicKeyTokenInTypeName

Exclude some properties from the serialization

By default are all public, non static, non read-only properties serialized. Fields are not serialized!

To exclude properties in your own types, you need to mark them with attributes.

1. ExcludeFromSerializationAttribute is supported out of the box.

public class MyClass
{
        [ExcludeFromSerialization]
        public int NotImportantProperty { get; set; }
}


2. If your objects are marked with common .NET Attributes such as XmlIgnoreAttribute you can add these attributes to the listing AttributesToIgnore.

serializer.PropertyProvider.AttributesToIgnore.Clear(); // remove default ExcludeFromSerializationAttribute for performance gain
serializer.PropertyProvider.AttributesToIgnore.Add(typeof(XmlIgnoreAttribute));

or using the settings:
var settings = new SharpSerializerBinarySettings(); // for binary mode
var settings = new SharpSerializerXmlSettings(); // for xml mode
settings.AdvancedSettings.AttributesToIgnore.Clear(); // remove default ExcludeFromSerializationAttribute for performance gain
settings.AdvancedSettings.AttributesToIgnore.Add(typeof(XmlIgnoreAttribute));


3. To exclude properties of the built in .NET types, or if you can not extend properties with attributes, you add the type and its property name to the list SharpSerializer.PropertyProvider.PropertiesToIgnore.

 i.e. System.Collections.Generic.List has property "Capacity" which is irrelevant for the serialization, thus it should be ignored.

serializer.PropertyProvider.PropertiesToIgnore.Add(typeof(List<string>), "Capacity");


PropertiesToIgnore can be also accessed in the sharpSerializer settings.

// create the settings
var settings = new SharpSerializerBinarySettings(); // for binary mode
var settings = new SharpSerializerXmlSettings(); // for xml mode
settings.AdvancedSettings.PropertiesToIgnore.Add(typeof(List), "Capacity");

Using custom PropertyProvider

If you need more control over the extracting of properties from an object, you can build your CustomPropertyProvider which derives from PropertyProvider and overrides its virtual methods GetAllProperties() and IgnoreProperty(...). In such a way you can additionally extract private or protected properties or handle properties which are marked with other Attributes like DataMemberAttribute, NonSerializedAttribute or XmlIgnoreAttribute.

serializer.PropertyProvider = new MyCustomPropertyProvider();

Using custom serializer and custom deserializer

1. Creating Serializer
The namespace Polenter.Serialization.Advanced contains some classes which are indispensable during serialization.

A) XmlPropertySerializer
serializes objects into a tree oriented structure, where every element has its beginning and end. XmlPropertySerializer is not responsible for serializing to xml. Instead it uses an instance of IXmlWriter to access the serialization stream.

DefaultXmlWriter implements IXmlWriter and contains the build in .NET XmlWriter which is responsible for writing to a stream.

To make your own tree oriented writer, you make a class which implements IXmlWriter

Polenter.Serialization.Advanced.Xml.IXmlWriter
                  jsonWriter = new MyJsonWriter();

this writer is passed to the constructor of the XmlPropertySerializer

Polenter.Serialization.Advanced.Serializing.IPropertySerializer
                  serializer =
new Polenter.Serialization.Advanced.XmlPropertySerializer(jsonWriter);

with this strategy pattern, the default XmlPropertySerializer can store data in any format which is tree oriented (contains begin/end tags)


B) BinaryPropertySerializer
serializes objects into elements with known length and fixed position in the stream. It doesn't write directly to the stream. Instead, it uses an instance of IBinaryWriter.

Actually there are two writers used by the SharpSerializer: BurstBinaryWriter and SizeOptimizedBinaryWriter

To make your own binary writer you make a class which implements IBinaryWriter.

Polenter.Serialization.Advanced.Binary.IBinaryWriter
                  compressedWriter = new MyVeryStrongCompressedAndEncryptedBinaryWriter();

this writer is passed to the constructor of the BinaryPropertySerializer

                  serializer = new Polenter.Serialization.Advanced.BinaryPropertySerializer(compressedWriter);



2. Creating Deserializer
The namespace Polenter.Serialization.Advanced contains classes which are counterparts of the above serializers/writers:

Serialization
Deserialization
XmlPropertySerializer XmlPropertyDeserializer
DefaultXmlWriter DefaultXmlReader
BurstBinaryWriter BurstBinaryReader
SizeOptimizedBinaryWriter SizeOptimizedBinaryReader


Syntax of creating Deserializers/Readers is analog to creating the Serializers/Writers, i.e.

Polenter.Serialization.Advanced.Binary.IBinaryReader
                  compressedReader = new MyVeryStrongCompressedAndEncryptedBinaryReader();

Polenter.Serialization.Advanced.Deserializing.IPropertyDeserializer
                  deserializer = new Polenter.Serialization.Advanced.BinaryPropertyDeserializer(compressedReader);



3. Overriding the default Serializer and Deserializer
The last step is creating SharpSerializer with an overloaded constructor

var sharpSerializer = new SharpSerializer(serializer, deserializer);


Changing only reader and writer in fixed serializer and deserializer is very easy extensibility model allowing serialization to/from almost any data format.