XmlSerializer和IEnumerable:序列化可能没有无参数构造函数:Bug?

时间:2011-01-18 15:55:18

标签: .net xml-serialization xmlserializer

在我们的项目中,我们可以扩展地使用XmlSerializer。我偶然发现了一个没有无参数构造函数的类。我认为这必须打破序列化过程,但事实并非如此。

通过研究这个问题我发现,在序列化/反序列化 IEnumerable 时,XmlSerializer表现得很奇怪:

  • 可枚举的所有元素都是序列化的
  • 该类需要实现添加(对象)方法
  • 它会忽略此类中可能存在的所有其他属性。
  • 使用此属性调用getter并重新使用返回的实例进行序列化(允许XmlSerializer使用无参数构造函数)。

请查看下面的示例。交叉部分是ODD1,ODD2。请注意,当我预期它们是真的时,good5和good6是假的。

这种行为有原因吗?

在手动实现IXmlSerializable时,是否可以让XmlSerializer重用属性返回的实例进行反序列化?

using System.Collections;
using System.Collections.Generic;   
using System.IO;    
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;

namespace Test
{
    public static class Program
    {
        public static void Main()
        {
            HostingClass host = new HostingClass();

            host.AutomaticSerialization.StringProperty = "AUTO";
            host.SelfImplementedSerialization.StringProperty = "SELF";

            bool good1 = host.AutomaticSerialization.FromConstructor == "PARAMETER";
            bool good2 = host.SelfImplementedSerialization.FromConstructor == "PARAMETER";
            bool good3 = host.AutomaticSerialization.StringProperty == "AUTO";
            bool good4 = host.SelfImplementedSerialization.StringProperty == "SELF";

            XmlSerializer serializer = new XmlSerializer(typeof(HostingClass));

            using (StringWriter sw = new StringWriter())
            {
                serializer.Serialize(sw, host);

                using (StringReader sr = new StringReader(sw.ToString()))
                {
                    host = (HostingClass)serializer.Deserialize(sr);
                }
            }

            bool good5 = host.AutomaticSerialization.FromConstructor == null; //is false
            bool good6 = host.AutomaticSerialization.StringProperty == "AUTO"; //is false

            bool good7 = host.SelfImplementedSerialization.FromConstructor == null;
            bool good8 = host.SelfImplementedSerialization.StringProperty == "SELF";

        }
    }

    public class HostingClass
    {
        private SelfImplementedSerialization _selfImplementedSerialization;
        public SelfImplementedSerialization SelfImplementedSerialization
        {
            get
            {
                return _selfImplementedSerialization
                       ?? (_selfImplementedSerialization = new SelfImplementedSerialization("PARAMETER"));
            }
            set { _selfImplementedSerialization = value; }
        }

        private AutomaticSerialization _automaticSerialization;
        public AutomaticSerialization AutomaticSerialization
        {
            get
            {
                return _automaticSerialization
                       ?? (_automaticSerialization = new AutomaticSerialization("PARAMETER")); //the returned object is used while deserializing
            }
            set { _automaticSerialization = value; }
        }
    }

    public class SelfImplementedSerialization : IXmlSerializable, IEnumerable<int>
    {
        public SelfImplementedSerialization() { }
        public SelfImplementedSerialization(string parameter)
        {
            FromConstructor = parameter;
        }

        public string StringProperty { get; set; }
        [XmlIgnore]
        public string FromConstructor { get; set; }

        public void ReadXml(XmlReader reader)
        {
            reader.ReadStartElement();
            StringProperty = reader.ReadElementString("StringProperty");
            reader.ReadEndElement();
        }

        public void WriteXml(XmlWriter writer)
        {
            writer.WriteElementString("StringProperty", StringProperty);
        }

        public IEnumerator<int> GetEnumerator()
        {
            yield return 1;
            yield return 2;
        }

        IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
        public XmlSchema GetSchema() { return null; }
    }


    public class AutomaticSerialization : IEnumerable<int>
    {
        //ODD1: Serialization possible w/o public parameterless constructor
        //public AutomaticSerialization() {} 
        public AutomaticSerialization(string parameter)
        {
            FromConstructor = parameter;
        }

        //ODD2: Element not serialized, only the IEnumerable Interface is serialized
        [XmlElement("SP")]
        public string StringProperty { get; set; }
        [XmlIgnore]
        public string FromConstructor { get; set; }

        public IEnumerator<int> GetEnumerator()
        {
            yield return 1;
            yield return 2;
        }

        public void Add(object o)
        {
            //requirement of XmlSerializer when serializing IEnumerables
        }

        IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
    }
}

1 个答案:

答案 0 :(得分:4)

这种行为的原因在于它始终有效。

来自XmlSerializer class

  

注意

     

XmlSerializer给予特殊   对实施的类的处理   IEnumerableICollection。一类   必须实现IEnumerable   实现一个公共的Add方法   只需一个参数。添加   method的参数必须相同   从Current返回的类型   返回值的属性   GetEnumerator,或其中一种类型   基地。实现的类   ICollection(例如CollectionBase)   除了IEnumerable必须有一个   public Item索引属性(索引器   在C#中取一个整数,它   必须具有公共Count属性   类型整数。 Add的参数   方法必须与原来的类型相同   从Item属性返回,或   这种类型的基础之一。对于课程   实现ICollection,值为   被序列化是从   索引的Item属性,而不是通过调用   GetEnumerator