XML Serialization of Generic Dictionary, Multidimensional Array, and Inherited Type, with sharpSerializer .NET






4.92/5 (47 votes)
How to serialize to XML, any generic, derived, or other complex type, which XMLSerializer cannot serialize
- Download sharpSerializer Windows DLL - 285.43 KB
- Download sharpSerializer sources and HelloWorld demo - 293.55 KB
- Download latest sources (external link)
Table of Contents
- Introduction
- Background
- Using the Code
- Hello World with
sharpSerializer
- Excluding properties from the serialization
- Customizing the property list for the serialization
- Serializing type as
AssemblyQualifiedName
or as a short type name "TypeName
,AssemblyName
" - Custom formatting of
DateTime
andfloat
values - Serialize data to other format as XML
- Hello World with
- How does sharpSerializer work?
- Examples of advanced serialization
- Serialize a polymorphic property (where the property value is inherited from the property type)
- Serialize a generic Dictionary where values are inherited from the interface
- Serialize a multidimensional array
- Serialize an array of different object types and an array of array (nested array)
- Serialize other types
- Restrictions of sharpSerializer
- Usage scenarios
- Download current sources
- Author's note
- History
Introduction
sharpSerializer
is an open source object serializer for .NET Framework, .NET Compact Framework and Silverlight. Its purpose is simple - quick object serialization from A to B, without security considerations. In this article, I'll try to convince you that sharpSerializer
can be a lot more than the built-in XMLSerializer and is simpler.
Background
The built-in XmlSerializer
has some restrictions concerning object serialization:
- It cannot serialize multidimensional arrays.
- It cannot serialize a generic
Dictionary<TKey,TValue>
. - It cannot serialize polymorphic properties (with an inherited value type from the property type) out of the box.
- It needs the type of the serialized object in its
Serialize(...)
method. - It needs many attributes to define your business objects, e.g.:
XmlArrayAttribute
,XmlArrayItemAttribute
to define the array of inherited types.
sharpSerializer
overcomes the above restrictions, and serializes objects with no need to mark your objects with additional attributes and without concerning types being serialized.
Using the Code
Hello World with sharpSerializer
Let's assume we have an object:
public class SomeObject
{
public int SimpleInt { get; set; }
public DateTime SimpleDateTime { get; set; }
public TimeSpan SimpleTimeSpan { get; set; }
public SimpleEnum SimpleEnum { get; set; }
public string SimpleString { get; set; }
}
public enum SimpleEnum {One,Two,Three}
We initialize and serialize this object:
// instantiate the object
var obj = new SomeObject(){
SimpleDateTime = new DateTime(2010,4,28),
SimpleEnum = SimpleEnum.Three,
SimpleInt = 42,
SimpleString = "nothing",
SimpleTimeSpan = new TimeSpan(1,2,3)};
// instantiate the sharpSerializer
// with standard constructor it serializes to XML
// overloaded constructors activate binary serialization
var serializer = new SharpSerializer();
// serialize
serializer.Serialize(obj, "test.xml");
// deserialize (to check the serialization)
var obj2 = serializer.Deserialize("test.xml");
Notice! You don't need to mark your object with any attribute, nor do you have to give the object type in the Serialize
method.
This object serializes to the following XML:
<Complex name="Root" type="SharpSerializerTestApp.SomeObject, SharpSerializerTestApp">
<Properties>
<Simple name="SimpleInt" value="42" />
<Simple name="SimpleDateTime" value="04/28/2010 00:00:00" />
<Simple name="SimpleTimeSpan" value="01:02:03" />
<Simple name="SimpleEnum" value="Three" />
<Simple name="SimpleString" value="nothing" />
</Properties>
</Complex>
As you can see, the object type was serialized as "TypeName
, AssemblyName
" and DateTime
was serialized as CultureInfo.InvariantCulture
. Later, I'll show you how to customize the type name (i.e., as AssemblyQualifiedName
) and how to save DateTime
and float
numbers in your desired format.
Excluding Properties from the Serialization
By default, all properties are serialized, which are public
, instance
, and not read-only. All properties which are arrays (Type.IsArray==true
), or which inherit from IEnumerable
, ICollection
or IDictionary
are also serialized. For performance reasons, fields are not serialized.
- To exclude properties in your custom types, you need to mark them with attributes.
ExcludeFromSerializationAttribute
is supported out of the box.public class MyClass { [ExcludeFromSerialization] public int SimpleInt { get; set; } }
- If your objects are marked with common .NET Attributes such as
XmlIgnore
, you can add these attributes to the listingAttributesToIgnore
.// remove default ExcludeFromSerializationAttribute for performance gain serializer.PropertyProvider.AttributesToIgnore.Clear(); serializer.PropertyProvider.AttributesToIgnore.Add(typeof(XmlIgnore));
or using the
settings
class:// for binary mode var settings = new SharpSerializerBinarySettings(); // for xml mode var settings = new SharpSerializerXmlSettings(); // remove default ExcludeFromSerializationAttribute for performance gain settings.AdvancedSettings.AttributesToIgnore.Clear(); settings.AdvancedSettings.AttributesToIgnore.Add(typeof(XmlIgnore));
- To exclude properties of the built in .NET types, or if you cannot extend properties with attributes, you add the type and its property name to the list
SharpSerializer.PropertyProvider.PropertiesToIgnore
.
i.e.System.Collections.Generic.List<T>
has propertyCapacity
which is irrelevant for the serialization, thus it should be ignored.serializer.PropertyProvider.PropertiesToIgnore.Add (typeof(List<string>), "Capacity");
PropertiesToIgnore
can be also accessed from thesharpSerializer
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");
Customizing the Property List for Serialization
If it is not enough to filter properties with ExcludeFromSerializationAttribute
, AttributesToIgnore
or PropertiesToIgnore
, you can build your CustomPropertyProvider
. As a base class, there is PropertyProvider
, which has two virtual methods GetAllProperties()
and IgnoreProperty(...)
. They can be overwritten to customize the logic.
serializer.PropertyProvider = new MyCustomPropertyProvider();
Serializing type as AssemblyQualifiedName or as a short type name "TypeName, AssemblyName"
Before SharpSerializer v.2.12, all types were serialized as short type name "TypeName
, AssemblyName
"; i.e.:
type="System.String, mscorlib"
This was simple to read, output size was small. But it encountered problems during deserialization if working with signed assemblies or their specific versions.
Since SharpSerializer v.2.12 are all types serialized as AssemblyQualifiedName
; i.e.:
type="System.String, mscorlib, Version=2.0.0.0,
Culture=neutral, PublicKeyToken=b77a5c561934e089"
You can alter the type naming by altering settings of sharpSerializer and setting the properties IncludeAssemblyVersionInTypeName
, IncludeCultureInTypeName
and IncludePublicKeyTokenInTypeName
. As default, these properties are set to true
.
var settings = new SharpSerializerXmlSettings();
settings.IncludeAssemblyVersionInTypeName = false;
settings.IncludeCultureInTypeName = false;
settings.IncludePublicKeyTokenInTypeName = false;
var serializer = new SharpSerializer(settings);
Custom Formatting of DateTime and Float Values
By default, all primitive types and DateTime
are converted to string
s according to CultureInfo.InvariantCulture
. If custom formatting or a culture is needed, this can be achieved with the following modification of the settings class:
var settings = new SharpSerializerXmlSettings();
settings.Culture = System.Globalization.CultureInfo.CurrentCulture;
var serializer = new SharpSerializer(settings);
Serialize Data to Other Format as XML
Actually, sharpSerializer
can serialize to XML and to its own binary format. However, after injecting it with custom IXmlWriter
or IBinaryWriter
it can serialize data to other text formats like Json or other encrypted, compressed, optimized, etc. binary streams. Please refer to the project site for details concerning binary serialization.
How Does sharpSerializer Work?
It works in three steps:
Step 1
The PropertyFactory
converts an object to the Property
. The abstract
class Property
contains PropertyCollection
. PropertyCollection
represents the object structure and its data. The following classes inherit from the Property
class:
SimpleProperty
for describing all primitive types andstring
,DateTime
,TimeSpan
and all enumerationsComplexProperty
for other classes and structures which are not listingsComplexReferenceProperty
for referencing a class which was already serialized (it saves space.)SingleDimensionalArrayProperty
for single dimensional arraysMultiDimensionalArrayProperty
for multidimensional arraysCollectionProperty
for listings which inherit fromICollection
but not fromIDictionary
DictionaryProperty
for listings which inherit fromIDictionary
NullProperty
for all objects/strings which arenull
(there is a need to make a notice that they arenull
)
Step 2
Property
is serialized by XmlPropertySerializer
. In which format it is serialized depends on the used writer.
Step 3
For XML serialization, DefaultXmlWriter is responsible
. But as a sink can be used any writer, which implements IXmlWriter
. In this way, you can write your own writers which serialize data to other formats like Json.
During deserialization, the process is inversed. An instance of IXmlReader
reads the data and forwards it to the XmlPropertyDeserializer
, which deserializes the data into Property
. Finally, ObjectFactory
converts Property
into object.
Please download the source code for more details.
Examples of Advanced Serialization
Following examples are parts of the HelloWorldDemo
application which you can download with the source code.
Serialize a Polymorphic Property (where Property Value is Inherited from the Property Type)
There is a property of type IComplexObject
. The property value contains a class ComplexObject
which is inherited from IComplexObject
.
public IComplexObject ComplexObject { get; set; }
There is no need to input the type being serialized. sharpSerializer
can serialize the expected type and any inherited type out of the box:
<Complex name="ComplexObject"
type="HalloWorldApp.BusinessObjects.ComplexObject, HalloWorldApp">
<Properties>
<Simple name="SimpleInt" value="33" />
</Properties>
</Complex>
Serialize a Generic Dictionary where Values are Inherited from the Interface
There is a generic dictionary where values are of some complex class which is inherited from the interface IComplexObject
(polymorphic argument):
public IDictionary<int, IComplexObject> GenericDictionary { get; set; }
It serializes to:
<Dictionary name="GenericDictionaryOfPolymorphicValues"
type="System.Collections.Generic.Dictionary`2[[System.Int32, mscorlib],
[HalloWorldApp.BusinessObjects.IComplexObject, HalloWorldApp]],
mscorlib" keyType="System.Int32, mscorlib"
valueType="HalloWorldApp.BusinessObjects.IComplexObject, HalloWorldApp">
<Items>
<Item>
<Simple value="2012" />
<Complex type="HalloWorldApp.BusinessObjects.ComplexObject, HalloWorldApp">
<Properties>
<Simple name="SimpleInt" value="2012000" />
</Properties>
</Complex>
</Item>
</Items>
</Dictionary>
As you can see, the type of keys and values does not matter. sharpSerializer
can serialize primitive types, complex objects, and even nested listings, e.g., dictionaries.
Serialize a Multidimensional Array
There is a two dimensional array (it could have more dimensions):
public string[,] DoubleArray { get; set; }
It serializes to:
<MultiArray name="DoubleArray" elementType="System.String, mscorlib">
<Dimensions>
<Dimension length="3" />
<Dimension length="2" />
</Dimensions>
<Items>
<Item indexes="0,0">
<Simple value="k1" />
</Item>
<Item indexes="0,1">
<Simple value="k2" />
</Item>
<Item indexes="1,0">
<Simple value="b1" />
</Item>
<Item indexes="1,1">
<Simple value="b2" />
</Item>
<Item indexes="2,0">
<Simple value="z1" />
</Item>
<Item indexes="2,1">
<Simple value="z2" />
</Item>
</Items>
</MultiArray>
It does not matter of what type the array is. sharpSerializer
can serialize an array of any object, array of arrays, array of collections, or an array of dictionaries. It can serialize really deep nested arrays.
Serialize an Array of Different Object Types and Array of Arrays (Nested Array)
There is an array of the following objects:
root.SingleArrayOfObjects = new object[]
{
42,
"nothing to say",
false,
BusinessObjects.SimpleEnum.Three,
null,
new object[]
{
42,
"nothing to say",
false,
BusinessObjects.SimpleEnum.Three,
null
}
};
It serializes to:
<SingleArray name="SingleArrayOfObjects" elementType="System.Object, mscorlib">
<Items>
<Simple type="System.Int32, mscorlib" value="42" />
<Simple type="System.String, mscorlib" value="nothing to say" />
<Simple type="System.Boolean, mscorlib" value="False" />
<Simple type="HalloWorldApp.BusinessObjects.SimpleEnum,
HalloWorldApp" value="Three" />
<Null />
<SingleArray type="System.Object[], mscorlib"
elementType="System.Object, mscorlib">
<Items>
<Simple type="System.Int32, mscorlib" value="42" />
<Simple type="System.String, mscorlib" value="nothing to say" />
<Simple type="System.Boolean, mscorlib" value="False" />
<Simple type="HalloWorldApp.BusinessObjects.SimpleEnum, HalloWorldApp"
value="Three" />
<Null />
</Items>
</SingleArray>
</Items>
</SingleArray>
Serialize Other Types
Please download the source code to see the whole example and what else can be serialized. Below are some other properties from the HelloWorldDemo
:
public class RootContainer
{
/// <summary>
/// Structures are handled as objects during serialization
/// They are serialized as ComplexProperty
/// </summary>
public AdvancedStruct AdvancedStruct { get; set; }
/// <summary>
/// Single dimensional array of simple type.
/// It is serialized as SingleDimensionalArrayProperty
/// </summary>
public string[] SingleArray { get; set; }
/// <summary>
/// Multidimensional array of simple type.
/// Is is serialized as MultiDimensionalArrayProperty
/// </summary>
public string[,] DoubleArray { get; set; }
/// <summary>
/// Single array of derived objects.
/// This is polymorphic collection - Items derive from the interface
/// </summary>
public IComplexObject[] PolymorphicSingleArray { get; set; }
/// <summary>
/// Generic list is serialized as a collection.
/// It is serialized as CollectionProperty
/// </summary>
public IList<string> GenericList { get; set; }
/// <summary>
/// Polymorphic property. Object instance derives from the property type
/// Is serialized as ComplexProperty
/// </summary>
public IComplexObject ComplexObject { get; set; }
/// <summary>
/// Collection where item values are
/// derived from the collection item type
/// </summary>
public ComplexObjectPolymorphicCollection ComplexObjectCollection
{ get; set; }
/// <summary>
/// Dictionary where values are derived
/// from the predefined dictionary value type
/// </summary>
public ComplexObjectPolymorphicDictionary ComplexObjectDictionary
{ get; set; }
/// <summary>
/// List items are derived from the generic attribute.
/// This is polymorphic attribute.
/// </summary>
public IList<IComplexObject> GenericListOfComplexObjects { get; set; }
/// <summary>
/// Generic object with polymorphic attribute.
/// It is serialized as ComplexProperty
/// </summary>
public GenericObject<IComplexObject> GenericObjectOfComplexObject
{ get; set; }
/// <summary>
/// Multidimensional array of generic object with polymorphic attribute
/// </summary>
public GenericObject<IComplexObject>[,]
MultiArrayOfGenericObjectWithPolymorphicArgument
{ get; set; }
}
public interface IComplexObject { int SimpleInt { get; set; } }
public class ComplexObject : IComplexObject {public int SimpleInt
{ get; set; }}
public class ComplexObjectPolymorphicCollection :
Collection<IComplexObject>{}
public class ComplexObjectCollection :
Collection<ComplexObject>{}
public class ComplexObjectPolymorphicDictionary :
Dictionary<int, IComplexObject>{}
public class ComplexObjectDictionary :
Dictionary<int, ComplexObject>{}
What do you think? Is XML serialization with sharpSerializer
simple enough?
Restrictions of sharpSerializer
In the current version of sharpSerializer
, there are some restrictions concerning serialization and deserialization of objects. In the future, these restrictions can be neutralized, but actually they make no such pain. These are:
- Objects without their
public
standard constructor cannot be deserialized. Multiple references to the same complex object are not optimized. Such multiple referenced object is serialized as many times as many references to it. (SharpSerializer
v.2.9 and above can optimize serializing of multiple references to the same object. Such an object is serialized only once. This optimization results with smaller file size.)
Following limitation concerns serialization in .NET Compact Framework and Silverlight:
LowerBound
in an array will always be deserialized as0
regardless of how it was serialized.
LowerBound
of an array is not a part of .NET Compact Framework or Silverlight and therefore cannot be handled by the sharpSerializer
.
Usage Scenarios
The main reason why I developed sharpSerializer
was to save application configuration in an XML file. Few years ago, I was struggling with a polymorphic configuration. I needed a lightweight configuration storage with support for object inheritance. The file should be easily readable and manually editable. The System.Configuration
has a big overhead and is too stiff.
The second reason is simple - Silverlight has poor support for the local data repository - it should have a better one :-).
Especially interesting is its possibility to serialize data to the binary format in WP7 (Windows Phone 7).
Download Current Sources
The most recent sources and news are on the project page: www.sharpserializer.com.
Author's Note
If you like sharpSerializer
or this article - please rate it. If not, please make a comment below ;-)
History
- 2011-11-09: Changes according to
sharpSerializer
v.2.16 - 2011-10-24: Updated download files
- 2011-07-31: Updated download files
SharpSerializer
v.2.12 - 2011-07-28: Updated download files
sharpSerializer
v.2.11 - 2011-05-08: Changes according to
sharpSerializer
v.2.9,Guid
serialization,AttributesToIgnore
and optimized serialization of multiple references to the same object - 2010-10-07: Updated download files
- 2010-10-03: Changes according to
sharpSerializer
v.2.0 - 2010-05-05: Changes according to
sharpSerializer
v.1.2 - 2010-05-04: Added support for the .NET Compact Framework
- 2010-04-30: Formatting changes (cut off some parts of the big XML example)
- 2010-04-29: First release