WP7 DataContractSerializer Private Members

Recently I found an issue using the DataContractSerializer on Windows Phone 7.  This isn't well documented and took some time to solve. Windows Phone 7 behaves differently to the desktop runtime when running the same code - hopefully for others googling this issue I am hitting the right keywords.

 

Under C#.NET you can define a class for serialization using something like the following:

...
using System.Runtime.Serialization;
...


// Class to serialize
[DataContract]
public class SomeClass
{
    [DataMember]
    public int ThisWillSerializeUnderWP7 { get; set; }

    [DataMember]
    private int ThisWontSerializeUnderWP7 { get; set; }
}

 

This class can then be serialized using a call something similar to:

...
using System.Runtime.Serialization;
...

// Create instance and set data
SomeClass data = new SomeClass();
data.ThisWillSerializeUnderWP7 = 1;

try
{ 
    // Serialize to Xml
    MemoryStream ms = new MemoryStream();
    DataContractSerializer serializer = new DataContractSerializer(typeof(SomeClass));
    serializer.WriteObject(ms, data);
}
catch (Exception ex)
{
    System.Diagnostics.Debug.WriteLine(ex);
}

 

Under the 'normal' Windows desktop C# runtime this works just fine. When running on the Windows Phone 7 runtime this call fails with an exception: "The data contract type 'WindowsPhoneGame1.SomeClass' is not serializable because it is not public. Making the type public will fix this error."

It appears that due to security restrictions placed on Windows Phone 7 applications / limitations of the compact framework, it is not possible to serialize or deserialize private members.

While others may have more elegant solutions I worked around this issue by:

  • Creating a single global Xml schema
  • Falling back to the back XmlSerializer
  • Classes derived from a common base class have Serialization / Deserialization methods
  • Using XmlSerializer means that elements in the Xml that are not in the class are ignored when deserializing
  • Using a custom serialize method means that fields in the class can be conditionally written to Xml when serializing
// Global schema
public class GameEntitySer
{
    #region Common
    #region Type
    public EEntityType EntityType { get; set; }
    ...
    #region SpinObjects
    public float SpinTime { get; set; }
    ...
}
// Base serializable object
abstract class GameEntity
{
    ...
    #region Fields
    // NOTE: Static to stop allocs during serialization
    private static GameEntitySer m_sSer = new GameEntitySer();   
    ...
    #region Methods
    public abstract void FromSer(GameEntitySer ser);
    public abstract GameEntitySer ToSer();

    protected GameEntitySer ToSerBase()
    {
        #region Type
        m_sSer.EntityType = m_EntityType;
        ...
        return m_sSer;
    }
    ...
}
// Derived serializable object
class GameEntitySpin : GameEntity
{
    private float m_SpinTime;
    ...
    public override GameEntitySer ToSer()
    {         
        GameEntitySer ser = ToSerBase();          
        ser.SpinTime = m_SpinTime;
    }
    ...
}
// Top level call to serialize
...
using System.Xml.Serialization;
...

var ser = new XmlSerializer(typeof(GameEntitySer));
using (XmlWriter xmlWriter = XmlWriter.Create(stream, xmlWriterSettings))
{
    // serObj is GameEntity (or derived) instance that contains data to be serialized
    GameEntitySer entitySer = serObj.ToSer();
    ser.Serialize(xmlWriter, list);
}