Re[2]: Xml сериализация и сохранение совместимости
От: MozgC США http://nightcoder.livejournal.com
Дата: 27.06.09 12:56
Оценка: 3 (1)
В общем вот я тут накидал все-таки вариант без заморочек.
Допустим сначала был такой класс конфига:

    [XmlRoot("config", IsNullable = false)]
    public class Config
    {
        private static readonly object _lockFlag = new object();
        private static Config _instance;
        private const string _configFilePath = "settings.xml";

        [XmlAttribute("ImportandData")]
        public string ImportandData { get; set; }

        [XmlArrayAttribute("DatesInfo")]
        public DateTime[] DatesInfo { get; set; }

        [XmlIgnore]
        public static Config Instance
        {
            get
            {
                lock (_lockFlag)
                {
                    if (_instance == null)
                    {
                        _instance = Load();
                    }
                }
                return _instance;
            }
        }

        private Config() { }

        public void Save()
        {
            var xmlSerializer = new XmlSerializer(typeof(Config));
            using (var writer = new StreamWriter(_configFilePath, false, System.Text.Encoding.GetEncoding("Windows-1251")))
            {
                xmlSerializer.Serialize(writer, this);
            }
        }

        private static Config Load()
        {
            if(!File.Exists(_configFilePath)) return new Config();

            var xmlSerializer = new XmlSerializer(typeof (Config));
            using (var xmlTextReader = new XmlTextReader(_configFilePath))
            {
                Config config = (Config) xmlSerializer.Deserialize(xmlTextReader);
                return config;
            }
        }

    }


Теперь вместо свойства public DateTime[] DatesInfo мы хотим сделать свойство public Date[] Dates. Для этого переделываем конфиг таким образом:

    [XmlRoot("config", IsNullable = false)]
    public class Config
    {
        private static readonly object _lockFlag = new object();
        private static Config _instance;
        private const string _configFilePath = "settings.xml";

        [XmlAttribute("ImportandData")]
        public string ImportandData { get; set; }

        ///<summary>This property is obsolete and is only used internally to support old config versions.</summary>
        [XmlArrayAttribute("DatesInfo")]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public DateTime[] DatesInfo
        {
            get
            { 
                throw new InvalidOperationException(
                    "This property is obsolete and is only used internally to support old config versions." );
            }
            set
            {
                var dates = from dt in value select new Date { Year = dt.Year, Month = dt.Month, Day = dt.Day };
                Dates = dates.ToArray();
            }
        }

        [XmlArrayAttribute("Dates")]
        public Date[] Dates { get; set; }

        [XmlIgnore]
        public static Config Instance
        {
            get
            {
                lock (_lockFlag)
                {
                    if (_instance == null)
                    {
                        _instance = Load();
                    }
                }
                return _instance;
            }
        }

        private Config() { }

        public void Save()
        {
            var attrs = new XmlAttributes { XmlIgnore = true };
            var attrOverrides = new XmlAttributeOverrides();
            attrOverrides.Add(typeof(Config), "DatesInfo", attrs);

            var xmlSerializer = new XmlSerializer(typeof(Config), attrOverrides);
            using (var writer = new StreamWriter(_configFilePath, false, System.Text.Encoding.GetEncoding("Windows-1251")))
            {
                xmlSerializer.Serialize(writer, this);
            }
        }

        private static Config Load()
        {
            if (!File.Exists(_configFilePath)) return new Config();

            var xmlSerializer = new XmlSerializer(typeof(Config));
            using (var xmlTextReader = new XmlTextReader(_configFilePath))
            {
                Config config = (Config)xmlSerializer.Deserialize(xmlTextReader);
                return config;
            }
        }
    }

    // ...

    public class Date
    {
        public int Year { get; set; }
        public int Month { get; set; }
        public int Day { get; set; }
    }


Все, при чтении старой версии конфига сеттер устаревшего свойства сконвертирует данные в новый тип и присвоит их новому свойству. А при сохранении конфига добавляем атрибут XmlIgnore устаревшему свойству так, что оно больше не запишется в конфиг. В геттере устаревшего свойства вместо исключения можно производить обратную конверсию — это уже по желанию.

Плюсы:
+ нет заморочек

Минусы:
— В классе конфига остается устаревшее свойство. Атрибут Obsolete нельзя применить, т.к. в таком случае сериалайзер не будет его читать.
Re[3]: Xml сериализация и сохранение совместимости
От: Kore Sar  
Дата: 27.06.09 14:15
Оценка:
Здравствуйте, MozgC, Вы писали:

MC>Все, при чтении старой версии конфига сеттер устаревшего свойства сконвертирует данные в новый тип и присвоит их новому свойству. А при сохранении конфига добавляем атрибут XmlIgnore устаревшему свойству так, что оно больше не запишется в конфиг. В геттере устаревшего свойства вместо исключения можно производить обратную конверсию — это уже по желанию.


MC>Плюсы:

MC>+ нет заморочек

MC>Минусы:

MC>- В классе конфига остается устаревшее свойство. Атрибут Obsolete нельзя применить, т.к. в таком случае сериалайзер не будет его читать.

Клёво!
Пара впоросов.
1) Атрибут XmlIgnore устаревшему свойству Вы забыли поставить. Верно?
2) Что значит Osboslete нельзя применять, потому что сериалайзер не будет его читать? Что значит "читать" в данном случае?
3) Для чего EditorBrowsable(EditorBrowsableState.Never)?
Re[4]: Xml сериализация и сохранение совместимости
От: MozgC США http://nightcoder.livejournal.com
Дата: 27.06.09 15:47
Оценка: 6 (1)
Здравствуйте, Kore Sar, Вы писали:

KS>Клёво!

KS>Пара впоросов.
KS>1) Атрибут XmlIgnore устаревшему свойству Вы забыли поставить. Верно?
Нет, внимательно посмотрите код метода Save(). Атрибут XmlIgnore для устаревшего свойства указывается в рантайме с помощью класса XmlAttributeOverrides.

KS>2) Что значит Osboslete нельзя применять, потому что сериалайзер не будет его читать? Что значит "читать" в данном случае?

Это значит будет вести себя так как будто этого свойства нет, или как будто на нем атрибут XmlIgnore. Т.е. при чтении старого конфига на новой версии программы, устаревшее свойство не будет устанавливаться, т.е. не будет вызываться его сеттер, а следовательно не будет происходить требуемой конвертации. В результате новое свойство при чтении старого конфига останется null.

KS>3) Для чего EditorBrowsable(EditorBrowsableState.Never)?

При использовании класса в коде другой сборки, или к примеру при включении соответствующего фильтра в ReSharper'е (ReSharper -> Options -> Intellisense -> Completion Appearance -> Filter members by [EditorBrowsable] attribute), свойства помеченные таким атрибутом не будут показываться в Intellisense, т.е. это дополнительно снизит вероятность попытки использования устаревшего свойства программистом который "не в курсе" что оно устарело. С этой же целью еще и XmlDoc добавлен.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.