XmlSerializer и IEnumerable: возможна сериализация без конструктора без параметров: Ошибка? - PullRequest
2 голосов
/ 18 января 2011

В нашем проекте мы широко используем XmlSerializer. Случайно я нашел класс без конструктора без параметров. Я думал, что это должно нарушить процесс сериализации, но это не так.

Исследуя эту проблему, я обнаружил, что XmlSerializer ведет себя странно при сериализации / десериализации IEnumerable :

  • Все элементы перечислимого сериализуются
  • Класс необходим для реализации Add (объект) метод
  • Он игнорирует все другие свойства, которые могут быть в этом классе.
  • Он вызывает метод получения с этим свойством и повторно использует возвращенный экземпляр для сериализации (что позволяет XmlSerializer работать без конструктора без параметров).

Пожалуйста, посмотрите на следующий пример. Интересными частями являются ODD1, ODD2. Обратите внимание, что good5 и good6 ложные, когда я ожидал, что они будут правдой.

Есть ли причина для такого поведения?

Можно ли заставить XmlSerializer повторно использовать экземпляр, возвращаемый свойством для десериализации при реализации IXmlSerializable вручную?

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 Ответ

4 голосов
/ 18 января 2011

Причиной такого поведения является то, что это всегда так.

С Класс XmlSerializer :

Примечание

XmlSerializer дает специальные лечение для классов, которые реализуют IEnumerable или ICollection. Класс который реализует IEnumerable должен реализовать публичный метод Add, который принимает один параметр. Добавить параметр метода должен быть одинаковым введите as, возвращаемый из Current свойство на значение, возвращаемое из GetEnumerator, или один из этого типа основы. Класс, который реализует ICollection (например, CollectionBase) в дополнение к IEnumerable должен иметь Свойство public Item indexed (индексатор) в C #), который принимает целое число, и это должен иметь публичное свойство Count введите целое число Параметр для добавления метод должен быть того же типа, что и возвращается из свойства Item или одна из основ этого типа. Для занятий которые реализуют ICollection, значения для быть сериализованы извлекаются из индексируемое свойство Item, не вызывая GetEnumerator.

...