实现IXmlSerializable需要集合属性有Setter

本文关键字:属性 Setter 集合 IXmlSerializable 实现 | 更新日期: 2023-09-27 18:06:21

我有一个自定义类型的集合属性,它继承自BindingList。目前,这个属性通过XmlSerializer序列化,即使它没有Setter。现在我尝试在这个自定义集合类上实现IXmlSerializable,并且看到WriteXml()和ReadXml()接口方法只有在集合属性具有Setter时才会被调用。为什么序列化现在忽略这个属性,除非在没有Setter的情况下正确序列化之前有Setter。

复制:

首先,创建一个名为"Item"的类:

public class Item
{
    public Item() {}
    // Generates some random data for the collection
    private MyCollectionType GenerateContent()
    {
        Random ranGen = new Random();
        MyCollectionType collection = new MyCollectionType();
        for (int i = 0; i < 5; i ++)
        {
            collection.Add("Item" + ranGen.Next(0,101));
        }
        return collection;
    }
    public MyCollectionType Items
    {
        get
        {
            if (m_Items == null)
            {
                m_Items = GenerateContent();
            }
            return m_Items;
        }
    }
    private MyCollectionType m_Items = null;
}

接下来创建集合类"MyCollectionType"(注意,在开始的代码片段中故意遗漏了IXmlSerializable):

public class MyCollectionType : BindingList<string>
{
    public MyCollectionType()
    {
        this.ListChanged += MyCollectionType_ListChanged;
    }
    void MyCollectionType_ListChanged(object sender, ListChangedEventArgs e){ }
    public MyCollectionType(ICollection<string> p)
    {
        this.ListChanged  += MyCollectionType_ListChanged;
    }
    #region Implementation of IXmlSerializable
    public void WriteXml(XmlWriter writer)
    {
        throw new NotImplementedException();
    }
    public void ReadXml(XmlReader reader)
    {
        throw new NotImplementedException();
    }
    public XmlSchema GetSchema() { return null; }
    #endregion
}

最后,在Main()中添加一些代码来序列化和反序列化一个"Item":

        Item myItem = new Item();
        Item newItem = null;
        // Define an XmlSerializer
        XmlSerializer ser = new XmlSerializer(typeof(Item));
        // Serialize the Object
        using (TextWriter writer = File.CreateText(@"c:'temporary'exportedfromtest.xml"))
        {
            ser.Serialize(writer,myItem);
        }
        // Deserialize it
        using (Stream reader = new FileStream(@"c:'temporary'exportedfromtest.xml", FileMode.Open, FileAccess.Read, FileShare.Read))
        {
            using (XmlDictionaryReader xmlDictionaryReader = XmlDictionaryReader.CreateTextReader(reader, XmlDictionaryReaderQuotas.Max))
            {
                newItem = (Item)ser.Deserialize(xmlDictionaryReader);
            }
        }

如果你按原样运行你应该看到它序列化和反序列化时没有Setter。目前,集合没有在上面的代码片段中列出"IXmlSerializable",但是方法在那里。因此,如果您现在返回并将"IXmlSerializable"添加到MyCollectionType类中并再次运行,您将注意到集合属性没有被序列化,并且没有调用WriteXml()和ReadXml()方法。还要注意,如果添加一个空Setter,这些方法将突然被调用。

实现IXmlSerializable需要集合属性有Setter

这是记录的行为。在介绍XML序列化中对此进行了解释,尽管不太清楚:

XML序列化不转换方法、索引器、私有字段或只读属性(只读集合除外)。要序列化对象的所有字段和属性(包括公共和私有),请使用DataContractSerializer而不是XML序列化。

可以看到,get-only属性通常是不序列化的——除了只读集合。但微软这么做是什么意思呢?集合终究不是一个属性。

它们的意思如下:

XML序列化不转换只能获取的属性或只读字段(除非具有预初始化集合的只能获取的集合值属性)

(顺便说一下,这意味着,如果您在包含类型的构造函数中向集合添加项,然后对其进行序列化和反序列化,则默认项将复制。有关原因的解释,请参见使用代码默认值的集合属性的XML反序列化。如果我反序列化你的Item类,我看到这个行为

这如何适用于你的情况?好吧,当你让你的集合实现IXmlSerializable时,序列化器不再把它解释为一个集合;它把它解释为一个黑盒子。因此,其特殊的托收规则不再适用。引用您的集合的属性现在必须为读/写;XmlSerializer将自己构造一个实例,反序列化它,并将它设置为父实例,就像其他非集合属性值一样。