Implementing IXmlSerializable Requires Collection Property to Have Setter

情到浓时终转凉″ 提交于 2020-01-11 09:46:12

问题


I have a collection property that is of a custom type which inherits from BindingList. Currently, this property gets serialized via XmlSerializer even though it has no Setter. I now am trying to implement IXmlSerializable on this custom collection class and see that the WriteXml() and ReadXml() interface methods only get called if my collection property has a Setter. Why does serialization ignore this property now unless there is a Setter when before it serialized correctly without one.

To Reproduce:

First, have a class called "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;
}

Next have create the collection Class "MyCollectionType" (Note that IXmlSerializable is purposely missing in the snippet to start off with):

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
}

Lastly, add some code in Main() to Serialize and Deserialize an "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);
            }
        }

So, if you run that as-is you should see that it serializes and deserializes without a Setter. Currently, the collection doesn't list "IXmlSerializable" in the snippet above, but the methods are there. So if you now go back and add "IXmlSerializable" to the MyCollectionType class and run again you will notice that the collection property isn't serialized and the WriteXml() and ReadXml() methods don't get called. Also note that if you add an empty Setter those methods will suddenly get called.


回答1:


This is the documented behavior. It is explained, albeit unclearly, in Introducing XML Serialization:

XML serialization does not convert methods, indexers, private fields, or read-only properties (except read-only collections). To serialize all an object's fields and properties, both public and private, use the DataContractSerializer instead of XML serialization.

As you can see, get-only properties are in general not serialized -- except for read-only collections. But what does Microsoft mean by this? A collection is not a property after all.

What they mean is as follows:

XML serialization does not convert get-only properties or read-only fields (except get-only collection-valued properties with pre-initialized collections).

(Incidentally, this means that, if you add items to your collection in the containing type's constructor, then serialize and deserialize it, the default items will get duplicated. For an explanation of why, see XML Deserialization of collection property with code defaults. If I deserialize your Item class I see this behavior.)

How does this apply in your case? Well, when you make your collection implement IXmlSerializable, the serializer no longer interprets it as a collection; it interprets it as a black box. Thus its special rules for collections no longer apply. The property referring to your collection must now be read/write; XmlSerializer will construct an instance itself, deserialize it, and set it into its parent just like any other non-collection property value.



来源:https://stackoverflow.com/questions/15662277/implementing-ixmlserializable-requires-collection-property-to-have-setter

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