I'd like to implement ISerializable for a C# class which contains a list of similar typed children. Consider the following example:
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
namespace serialisation
{
[Serializable]
internal class Nested : ISerializable
{
public string Name { get; set; }
public List<Nested> Children { get; set; }
public Nested(string name)
{
Name = name;
Children = new List<Nested>();
}
protected Nested(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
{
Name = info.GetString("Name");
// This doesn't work:
Nested[] children = (Nested[])info.GetValue("Children", typeof(Nested[]));
Children = new List<Nested>(children);
// This works:
// Children = (List<Nested>)info.GetValue("Children", typeof(List<Nested>));
}
public void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
{
info.AddValue("Name", Name);
// This doesn't work:
info.AddValue("Children", Children.ToArray());
// This works:
//info.AddValue("Children", Children);
}
}
internal class Program
{
private static void Main(string[] args)
{
// Generate a hierarchy
Nested root = new Nested("root");
Nested child1 = new Nested("child1");
Nested child2 = new Nested("child2");
Nested child3 = new Nested("child3");
child1.Children.Add(child2);
child1.Children.Add(child3);
root.Children.Add(child1);
Nested deserialized;
BinaryFormatter binaryFmt = new BinaryFormatter();
// Serialize
using (var fs = new FileStream("Nested.xml", FileMode.OpenOrCreate))
{
binaryFmt.Serialize(fs, root);
}
// Deserialize
using (var fs = new FileStream("Nested.xml", FileMode.OpenOrCreate))
{
deserialized = (Nested)binaryFmt.Deserialize(fs);
}
// deserialized.Children contains one null child
Console.WriteLine("Original Name: {0}", root.Name);
Console.WriteLine("New Name: {0}", deserialized.Name);
}
}
}
In the sample above, Nested.GetObjectData and the serializer constructor for Nested are invoked 4 times, one after another.
Adding the children to the serializer as a Nested array will return a correctly sized array on de-serialization, but all the elements will be null.
However, changing the type from Nested array to Nested List will magically fix up the null elements after the constructors for the children have been called.
What I'd like to know is:
- What's special about Nested List?
- What is the recommended way to serialize a class with a recursive structure such as this?
Update:
It seems there is an additional interface, IDeserializationCallback.OnDeserialization, which is called after de-serialization has taken place (the calling order is non-deterministic). You can store the de-serialized array in a temp member variable in the constructor and then assign it to a list in this method. Unless I'm missing something, this seems less than ideal as you must clutter your implementation with temp vars.