This follows on from my previous question Serialize list of interfaces using XML Serialization
public class MeterWalkOrder
{
public MeterWalk
The problem is that XmlSerializer
deserializes a property referring to a class implementing IList<T>
in the following way:
It calls the getter to get the list. If null, it allocates a list and sets it via the setter. It holds onto the list in some local variable while reading it.
It deserializes each list element, and adds it to the list it is holding.
And that's it. It never calls the containing class's list property setter afterwards.
You can verify this by replacing your List<Meter>
with an ObservableCollection<Meter>
, and setting a debug listener for when the collection changes:
[XmlArrayItem(ElementName = "Meter")]
[XmlArray(ElementName = "Meters")]
public ObservableCollection<Meter> SerializableMeters
{
get
{
Debug.WriteLine("Returning proxy SerializableMeters");
var list = new ObservableCollection<Meter>(Meters.Cast<Meter>());
list.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(list_CollectionChanged);
return list;
}
set
{
Debug.WriteLine("Setting proxy SerializableMeters");
Meters = new List<IMeter>(value.Cast<IMeter>());
}
}
static void list_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
var collection = (IList<Meter>)sender;
Debug.WriteLine("Proxy collection changed to include : ");
foreach (var item in collection)
Debug.WriteLine(" " + item.ToString());
}
Doing so, you'll see the following debug output:
Returning proxy SerializableMeters
Returning proxy SerializableMeters
Returning proxy SerializableMeters
Returning proxy SerializableMeters
Proxy collection changed to include :
Meter: 1, 12345
Proxy collection changed to include :
Meter: 1, 12345
Meter: 2, SE
As you can see, the list is never set back.
Luckily, there's an easy alternative. If you return a proxy array instead of a proxy List, XmlSerializer
will allocate the array itself, populate it, and set it via the setter -- which is just what you want!
[XmlArrayItem(ElementName = "Meter")]
[XmlArray(ElementName = "Meters")]
public Meter [] SerializableMeters
{
get
{
return Meters.Cast<Meter>().ToArray();
}
set
{
Meters = new List<IMeter>(value.Cast<IMeter>());
}
}
And then later
var meters = xml.LoadFromXML<MeterWalkOrder>();
Debug.Assert(meters.Meters.Count == 2); // No assert.