Cannot deserialize XML into a list using XML Deserializer

前端 未结 1 1031
清歌不尽
清歌不尽 2020-12-07 06:36

This follows on from my previous question Serialize list of interfaces using XML Serialization

    public class MeterWalkOrder
    {
        public MeterWalk         


        
相关标签:
1条回答
  • 2020-12-07 06:50

    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.
    
    0 讨论(0)
提交回复
热议问题