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 IncludeAssemblyVersionInTypeName, IncludeCultureInTypeName 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.