What is the right way to (de)serializer complex types with MobileFormatter?

What is the right way to (de)serializer complex types with MobileFormatter?

Old forum URL: forums.lhotka.net/forums/t/8297.aspx


markell posted on Thursday, January 07, 2010

Dear ladies and sirs.

I have a question on the correct use of the MobileFormatter when the (de)serialized types are complex.
I want to demonstrate it by an example.
[Serializable]
public class O : IMobileObject
{
  private static string GetShortAssemblyQualifiedTypeName(Type type)
  {
    return string.Format(CultureInfo.InvariantCulture, "{0}, {1}",
      type.FullName, new AssemblyName(type.Assembly.FullName).Name);
  }
  public O() { }
  public IList<Type> Types { get; set; }
  public void GetState(SerializationInfo info) { }
  public void SetState(SerializationInfo info) { }
  public void GetChildren(SerializationInfo info, MobileFormatter formatter)
  {
    Func<Type, string> mapper = GetShortAssemblyQualifiedTypeName;
    var names = new MobileList<string>(Types.Select(mapper));
    info.AddChild("Types", formatter.SerializeObject(names).ReferenceId);
  }
  public void SetChildren(SerializationInfo info, MobileFormatter formatter)
  {
    var names = (IList<string>)formatter.GetObject(info.Children["Types"].ReferenceId);
    Types = ((IList<string>)names).Select(name => Type.GetType(name, true)).ToList();
  }
}

It contains a list of Type objects. Since it is not recommended to serialize Type objects, I first convert them to a list of Type names.
Of course, this implementation does not work, because O.SetChildren is called before the Types list is deserialized. This comes as a surprise to someone used to ordinary .NET serialization. It appears that one must override the ISerializationNotification interface in order to get it work right, so here is my final working version (changes are in red):
[Serializable]
public class O : IMobileObject, ISerializationNotification
{
  private static string GetShortAssemblyQualifiedTypeName(Type type)
  {
    return string.Format(CultureInfo.InvariantCulture, "{0}, {1}",
      type.FullName, new AssemblyName(type.Assembly.FullName).Name);
  }
  public O() { }
  private object m_types;
  public IList<Type> Types
  {
    get { return (IList<Type>)m_types; }
    set { m_types = value; }
  }
  public void GetState(SerializationInfo info) { }
  public void SetState(SerializationInfo info) { }
  public void GetChildren(SerializationInfo info, MobileFormatter formatter)
  {
    Func<Type, string> mapper = GetShortAssemblyQualifiedTypeName;
    var names = new MobileList<string>(Types.Select(mapper));
    info.AddChild("Types", formatter.SerializeObject(names).ReferenceId);
  }
  public void SetChildren(SerializationInfo info, MobileFormatter formatter)
  {
    m_types = (IList<string>)formatter.GetObject(info.Children["Types"].ReferenceId);
  }
  public void Deserialized()
  {
    Types = ((IList<string>)m_types).Select(name => Type.GetType(name, true)).ToList();
  }
}

It has an ugly hack of reusing the same field for holding IList<Type> and IList<string>, the latter is held between the call to O.SetChildren and O.Deserialized.

My question is this how it is supposed to be? Or there is another, the right way?
Thanks.

markell replied on Wednesday, January 13, 2010

Bump bump bump ....

RockfordLhotka replied on Wednesday, January 13, 2010

The MobileFormatter is not necessarily a general-purpose serializer. It is designed specifically to enable the CSLA object scenarios.

You may be able to get it to work outside those scenarios, and if so, good for you! :)

MobileFormatter understands how to serialize primitive types, some special types (like Guid, Decimal, etc) that we consider "primitive" and types that implement IMobileObject. That's it. If you have a type that doesn't fit into one of those categories, then you are outside the design parameters of MobileFormatter.

Copyright (c) Marimer LLC