While I was learning and writing my first WP7 application I realized that understanding the serialization mechanism is vital if you want to write a WP7 application. You will probably end up using it when persisting data to the isolated storage and (implicitly) when you save the application state to the page state or to the global application state object.

So I’m writing this post as a reminder to myself and to summarize some of the key points of the matter.

WP7 actually supports two different types of serialization:

  • Binary Serialization - this is the fastest and most compact form of serialization, but it’s also the less flexible one. 
    (as correctly pointed out by Greg Finzer there's no built-in binary serialization in WP7, you need to write your own).
  • Xml Serialization.
  • DataContract Serialization - this is the default method used by the framework when it serializes and deserializes objects to the different application state dictionaries.

I will not dig into every detail of them because there is already a lot of documentation around (especially in MSDN). The rest of the post will mainly deal with DataContract serialization.

To be able to use the DataContractSerializer effectively you have to remind that:

  • You can serialize public types, fields and properties; your properties must have a getter and a setter in order to be serialized (you will incur in an exception if you try to serialize a public property without a getter).
  • DataContractSerializer works with an opt-in philosophy, which means that you need to specify what you want to serialize using [DataContract] (for classes) and [DataMember] (for field and properties) attributes.
  • If you do not specify the [DataContract] and [DataMember] attributes and your type is a public type, any public property and field will be serialized.
  • You can ‘break the rule’ and persist an internal class or internal members if you mark the assemblies containing your types with the [InternalsVisibleTo] attribute with something like:
    [assembly: InternalsVisibleTo("System.Runtime.Serialization")]

    but this isn’t recommended and the documentation explicitly says that this attribute should not be used directly in our code. If you do not specify that attribute you will get a SecurityException when trying to serialize an internal class.
  • During deserialization the object Constructor will not be called. So if you have part of you object initialization logic in the constructor do not expect it to be called during deserialization. You will need to refactor it to a method and call it after the object deserialize. The following code snippet shows an example:
    [DataContract]
    public class EntityWithConstructor
    {
    	public string M1;
    
    	[DataMember]
    	public int P1 { get; set; }
    
    	public EntityWithConstructor()
    	{
    		M1 = "Intialized";
    	}
    }
    
    ...
    // object constructor is not called during deserialization
    EntityWithConstructor obj = new EntityWithConstructor();
    string data = DataContractSerializerHelpers.Serialize(obj);
    MessageBox.Show(data);
    EntityWithConstructor deser = DataContractSerializerHelpers.Deserialize(data);
    // deser.M1 will be "", instead of the "Initialized" specified in the parameterless contructor
    MessageBox.Show("Constructor initialized value (it should be 'Initialized': " + deser.M1);
    
  • Object references aren’t preserved by default: if you need to preserve object identity while persisting complex hierarchies you need to set the ‘IsReference’ parameter to true in the [DataContract] attribute declaration. This helps a lot preventing the circular references problem that may arise with cross linked objects. As an example check the following code:
    [DataContract]
    public class Entity
    {
    	public int M1;
    
    	[DataMember]
    	public string P1 { get; set; }
    }
    
    [DataContract(IsReference = true)]
    public class IsReferenceEntity
    {
    	public int M1;
    
    	[DataMember]
    	public string P1 { get; set; }
    }
    
    public class EntityContainer
    {
    	public List Entities;
    
    	public Entity SelectedEntity;
    
    	public List IsReferenceEntities;
    
    	public IsReferenceEntity SelectedIsReferenceEntity;
    }
    ...
    // references are preserved if you specify the 'IsReference = true' parameter of DataContract attribute
    var cont = new EntityContainer();
    cont.Entities = new List {new Entity() { P1 = "E1"}};
    cont.SelectedEntity = cont.Entities[0];
    cont.IsReferenceEntities = new List() {new IsReferenceEntity(){ P1 = "Ref1"}};
    cont.SelectedIsReferenceEntity = cont.IsReferenceEntities[0];
    
    string data = DataContractSerializerHelpers.Serialize(cont);
    MessageBox.Show(data);
    EntityContainer deser = DataContractSerializerHelpers.Deserialize(data);
    
    bool isEqualEntityP1Content = deser.Entities[0].P1 == deser.SelectedEntity.P1; // true
    bool isSameEntityInstance = deser.Entities[0] == deser.SelectedEntity; //false
    
    bool isEqualIsReferenceEntityP1Content = deser.IsReferenceEntities[0].P1 == deser.SelectedIsReferenceEntity.P1; //true
    bool isSameIsReferenceEntityInstance = deser.IsReferenceEntities[0] == deser.SelectedIsReferenceEntity; //true
    
    string message =
    	string.Format(
    		"Entity\n - same content: {0}\n - same reference: {1} \n\nIsReferenceEntity\n - same content: {2}\n - same reference: {3}",
    		isEqualEntityP1Content, isSameEntityInstance, isEqualIsReferenceEntityP1Content, isSameIsReferenceEntityInstance);
    MessageBox.Show(message);
  • The DataContractSerializer is able to persist inherited class hierarchies, but If the objects you are persisting have members that point to the base class types, you will need to mark the classes you are saving with the [KnownTypes] attribute to inform the DataContractSerializer of all the types involved in the serialization process:
    [DataContract]
    public class Entity
    {
    	public int M1;
    
    	[DataMember]
    	public string P1 { get; set; }
    }
    
    [DataContract]
    public class Derived : Entity
    {
    	[DataMember]
    	public string P2 { get; set; }
    }
    
    [DataContract]
    [KnownType(typeof(Derived))]
    public class DerivedContainer
    {
    	[DataMember]
    	public Entity Entity { get; set; }
    }
    ...
    DerivedContainer cont = new DerivedContainer();
    cont.Entity = new Derived() {P2 = "data"};
    
    string data = DataContractSerializerHelpers.Serialize(cont);
    MessageBox.Show(data);
    DerivedContainer deser = DataContractSerializerHelpers.Deserialize(data);
    // deser.Entity is of type Derived.
    MessageBox.Show("Type inside Entity field: " + deser.Entity.GetType().Name);
    

These examples should cover most of the usual usage sceneries of Data Contract Serialization. Attached to this article you can find a simple project that covers all the illustrated cases.

There are however some more considerations I’d like to add and they are specific to WP7, it’s very important to understand WHEN the WP7 framework performs its automatic serialization and deserialization stages and where to look for serialization errors.

When you add an object in one of the two State Dictionaries (being it the one in PhoneApplicationPage or the one in PhoneApplicationServices) it does not get immediately serialized; the actual operation is delayed until you navigate away from the page or your application gets ‘tombstoned’.

In a similar way the deserialization happens when you navigate to the page (for the Page.State dictionary) and when the application is reactivated after the ‘tombstoning’.

Due to these considerations do not expect to have immediate feedback on eventual serialization errors of your objects when you add them to the state dictionaries, you will need to navigate away from the page or switch to another application to get notified by errors.

A place to handle these kinds of errors is the ‘UnhandledException’ event of the application class because the serialization stage is outside your direct control (unless you use a custom serialization mechanism before adding the objects to the state dictionaries). But if this happens your application will for sure be in an unstable state.

To avoid these errors, if you are using the state dictionaries to persist complex data structures (like some ViewModels for example), I’d suggest to create a very simple DataContractSerializer helper class (like the one you can find inside the test project) and use it to setup a project to test if your objects can be correctly serialized by the framework.

Sample project: