Cannot deserialize XML into a list using XML Deserializer

倖福魔咒の 提交于 2019-11-28 02:27:19

The problem is that XmlSerializer deserializes a property referring to a class implementing IList<T> in the following way:

  1. 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.

  2. It deserializes each list element, and adds it to the list it is holding.

  3. 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.
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!