【问题标题】:Cannot deserialize XML into a list using XML Deserializer无法使用 XML Deserializer 将 XML 反序列化为列表
【发布时间】:2014-10-03 13:23:43
【问题描述】:

这是我之前的问题Serialize list of interfaces using XML Serialization

    public class MeterWalkOrder
    {
        public MeterWalkOrder()
        {
            Meters = new List<IMeter>();
        }

        public String Name { get; set; }

        [XmlIgnore]
        public List<IMeter> Meters { get; set; }

        [XmlArrayItem(ElementName = "Meter")]
        [XmlArray(ElementName = "Meters")]
        public List<Meter> SerializableMeters
        {
            get
            {
                return Meters.Cast<Meter>().ToList();
            }
            set
            {
                Meters = new List<IMeter>(value);                
            }
        }
    }

     public interface IMeter {
       int MeterID { get; set; }
     }

     public class Meter : IMeter {
         public int MeterID { get; set; }
         public string SerialNumber { get; set; }
     }
}

我正在使用下面的扩展方法将 XML 反序列化回我的对象​​(理想情况下,我希望扩展方法脱离对象,但我对扩展方法不太满意,所以我现在就这样离开了)。 ..

public static class SerializationExtensions
{

    public static T LoadFromXML<T>(this string xmlString)
    {
        T returnValue = default(T);

        XmlSerializer serial = new XmlSerializer(typeof(T));
        StringReader reader = new StringReader(xmlString);
        object result = serial.Deserialize(reader);

        if (result != null && result is T)
        {
            returnValue = ((T)result);
        }

        reader.Close();

        return returnValue;
    }

....但是,当我在下面给出 XML 时....

<?xml version="1.0"?>
<MeterWalkOrder xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Name>Red Route</Name>
  <Meters>
    <Meter>
      <MeterID>1</MeterID>
      <SerialNumber>12345</SerialNumber>
    </Meter>
    <Meter>
      <MeterID>2</MeterID>
      <SerialNumber>SE</SerialNumber>
    </Meter>
  </Meters>
</MeterWalkOrder>

没有仪表被填充?

有谁知道什么会导致这个问题? XML 是有效的,SerializeableMeters 只是一个读取和写入 Meters 的属性,但由于在序列化中使用接口的已知问题,将其转换为具体类

【问题讨论】:

  • 为什么需要 SerializableMeters?为什么不直接序列化 Meters 属性?
  • 我不能直接序列化它,因为它是一个不受支持的接口列表。我不想将其更改为使用具体类,因为我们总是对接口而不是具体类进行编码

标签: c# deserialization xmlserializer


【解决方案1】:

问题在于XmlSerializer 反序列化了引用实现IList&lt;T&gt; 的类的属性,方法如下:

  1. 它调用 getter 来获取列表。如果为 null,它分配一个列表并通过 setter 设置它。它在读取列表时保留在某个局部变量中的列表。

  2. 它反序列化每个列表元素,并将其添加到它所持有的列表中。

  3. 就是这样。之后它永远不会调用包含类的 list 属性设置器

您可以通过将您的List&lt;Meter&gt; 替换为ObservableCollection&lt;Meter&gt; 来验证这一点,并在集合更改时设置一个调试侦听器:

    [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());
    } 

这样做,您将看到以下调试输出:

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

如您所见,列表永远不会退缩。

幸运的是,有一个简单的替代方案。如果你返回一个代理 array 而不是一个代理 ListXmlSerializer 将自己分配数组,填充它,然后通过 setter 设置它——这正是你想要的!

    [XmlArrayItem(ElementName = "Meter")]
    [XmlArray(ElementName = "Meters")]
    public Meter [] SerializableMeters
    {
        get
        {
            return Meters.Cast<Meter>().ToArray();
        }
        set
        {
            Meters = new List<IMeter>(value.Cast<IMeter>());
        }
    }

后来

    var meters = xml.LoadFromXML<MeterWalkOrder>();
    Debug.Assert(meters.Meters.Count == 2); // No assert.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-09-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多