实现IXmlSerializable需要集合属性具有Setter

3
我有一个集合属性,它是从BindingList继承的自定义类型。目前,即使它没有Setter,此属性也通过XmlSerializer进行序列化。我现在正在尝试在这个自定义集合类上实现IXmlSerializable,并且发现只有当我的集合属性有Setter时,WriteXml()和ReadXml()接口方法才会被调用。为什么现在序列化忽略了这个属性,除非有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,那么这些方法将会突然被调用。


这与WCF有什么关系?请负责任地标记 :) - Tim
请发布代码。 - Dustin Kingen
1个回答

3
这是已记录的行为。在介绍 XML 序列化中,虽然不够清晰,但已经解释了:

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

可以看到,只读属性通常不会被序列化——但是对于只读集合则不同。但是,微软这是什么意思呢?毕竟集合并不是一个属性。

他们的意思如下:

XML 序列化不会转换只读字段或只读的 get 属性(除带有预初始化集合的只读集合值属性外)

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

在您的情况下如何应用呢?当您的集合实现 IXmlSerializable 时,序列化程序不再将其解释为集合;它将其解释为一个黑盒子。因此,它不再适用于集合的特殊规则。指向您的集合的属性现在必须是可读/写的;XmlSerializer 将自己构造一个实例、对其进行反序列化,并将其设置到其父级中,就像任何其他非集合属性值一样。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接