У нас есть класс, который представляет собой настройки, введённые пользователем. Что-то типа такого:
class SettingsContainer
{
public string Name { get; set; }
public int Age { get; set; }
public List<SpecialDay> SpecialDays { get; set; }
public MySerializableDictionaty<string, string> SomeData { get; set; }
// ........ тут такого еще пара десятков
}
class SpecialDay
{
public bool IsRepeating { get; set; }
public DateTime Date { get; set; }
public string Name { get; set; }
}
// ... еще несколько подобных классов
Планируется сериализировать в отдельный Xml файл, чтобы у пользователя была возможность иметь хоть десяток таких файлов. Обязательным условием есть следующее: Все последующие версии приложения обязаны быть совместимыми, т.е. любая новая версия должна читать настройки любой предыдущей версии.
Т.е. надо сделать так, чтобы при переименовании свойства, переименовании класса, изменении типа, добавлении и удалении (это не проблема, как я понимаю) всего этого всё было обратно совместимым.
Какие посоветуете сделать шаги на стадии имплементации этого чуда? Какие будут подводные камни в будущем?
Здравствуйте, Kore Sar, Вы писали:
KS>Планируется сериализировать в отдельный Xml файл, чтобы у пользователя была возможность иметь хоть десяток таких файлов. Обязательным условием есть следующее: KS>Все последующие версии приложения обязаны быть совместимыми, т.е. любая новая версия должна читать настройки любой предыдущей версии. KS>Т.е. надо сделать так, чтобы при переименовании свойства, переименовании класса, изменении типа, добавлении и удалении (это не проблема, как я понимаю) всего этого всё было обратно совместимым. KS>Какие посоветуете сделать шаги на стадии имплементации этого чуда? Какие будут подводные камни в будущем? KS>Заранее спасибо всем ответившим по делу.
Ну первое что приходит в голову — а зачем что-то переименовывать? Нет, вы, конечно, можете изменить название самого свойства класса, но при этом просто не нужно менять название элемента в которое это свойство сериализуется.
При измении типов определяйте для новых типов конверсию, т.е. чтобы MyOldType можно было бы привести к MyNewType.
Но это в том случае, если использовать стандартную XML-сериализацию.
Я бы в данном случае сделал сериализатор ручками. Причем сериализация бы происходила по манифесту, описанному в том же XML-формате. Манифест бы тупо маппил ХМЛ-элементы на классы, ХМЛ-атрибуты на св-ва и пр.
В будущей версии можно было бы к примеру *полностью* переделать всю объектную модель настроек, но написать специальный манифест для совместимости, с помощью которого можно было бы грузить старые конфиги.
Плюс не было бы всего этого уродства связанного со стандартной ХМЛ-сериализацией — конструкторы с параметрами нельзя, абстрактные классы нельзя и проч. и проч.
Re[2]: Xml сериализация и сохранение совместимости
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Здравствуйте, Kore Sar, Вы писали:
ВВ>Ну первое что приходит в голову — а зачем что-то переименовывать? Нет, вы, конечно, можете изменить название самого свойства класса, но при этом просто не нужно менять название элемента в которое это свойство сериализуется.
Жизнь такая. Всё может быть. Перестраховуюсь.
ВВ>При измении типов определяйте для новых типов конверсию, т.е. чтобы MyOldType можно было бы привести к MyNewType.
Тут не понял. Какая именно конверсия?
ВВ>Но это в том случае, если использовать стандартную XML-сериализацию.
ВВ>Я бы в данном случае сделал сериализатор ручками. Причем сериализация бы происходила по манифесту, описанному в том же XML-формате. Манифест бы тупо маппил ХМЛ-элементы на классы, ХМЛ-атрибуты на св-ва и пр.
Т.е. рефлексия + парсинг ХМЛ-ки + парсинг манифеста ХМЛ-ного. Можно подумать. Спасибо за идею.
ВВ>В будущей версии можно было бы к примеру *полностью* переделать всю объектную модель настроек, но написать специальный манифест для совместимости, с помощью которого можно было бы грузить старые конфиги.
ВВ>Плюс не было бы всего этого уродства связанного со стандартной ХМЛ-сериализацией — конструкторы с параметрами нельзя, абстрактные классы нельзя и проч. и проч.
Конструкторы — это мелочь.
Абстрактные можно. XmlTypeAttribute для этого есть.
Какое еще там уродство есть, кроме уже перечисленного?
Просто задавайте имена XML-атрибутов с помощью атрибутов семейства XmlAttribute (XmlRootAttribute, XmlAttribute, XmlArrayAttribute и т.д.).
После можете перименовывать свойства и классы, все будет работать как нужно. По удаления или добавления свойства/типа — это не проблема — к примеру при добавлении нового свойства сериалайзер просто не проинициализирует его из старого конфига, на такой случай надо задавать default-значение свойства. В общем у меня тоже конфиг постепенно рос и менялся, за несколько лет проблем не было.
В принципе проблем не будет даже если вы немного измените тип данных, к примеру был int стал string — конфиг все равно прочитается без ошибок, просто из при чтении соответствующего атрибута число которое там хранилось уже будет читаться как строка. Если же было к примеру свойство public List<SpecialDay> SpecialDays { get; set; } а станет List<DateTime> SpecialDays { get; set; } то конечно будут проблемы. Лично я бы в таком случае создал бы новое свойство в классе SettingsContainer, а в сеттере старого свойства сделал бы конвертацию и записывал бы данные уже в новое. При сохранении конфига можно рефлекшеном проставить атрибут XmlIgnore на такое deprecated свойство.
Re[2]: Xml сериализация и сохранение совместимости
Здравствуйте, MozgC, Вы писали:
MC>Просто задавайте имена XML-атрибутов с помощью атрибутов семейства XmlAttribute (XmlRootAttribute, XmlAttribute, XmlArrayAttribute и т.д.). MC>После можете перименовывать свойства и классы, все будет работать как нужно. По удаления или добавления свойства/типа — это не проблема — к примеру при добавлении нового свойства сериалайзер просто не проинициализирует его из старого конфига, на такой случай надо задавать default-значение свойства. В общем у меня тоже конфиг постепенно рос и менялся, за несколько лет проблем не было. MC>В принципе проблем не будет даже если вы немного измените тип данных, к примеру был int стал string — конфиг все равно прочитается без ошибок, просто из при чтении соответствующего атрибута число которое там хранилось уже будет читаться как строка. Если же было к примеру свойство public List<SpecialDay> SpecialDays { get; set; } а станет List<DateTime> SpecialDays { get; set; } то конечно будут проблемы. Лично я бы в таком случае создал бы новое свойство в классе SettingsContainer, а в сеттере старого свойства сделал бы конвертацию и записывал бы данные уже в новое. При сохранении конфига можно рефлекшеном проставить атрибут XmlIgnore на такое deprecated свойство.
Очень полезно. Спасибо.
Re[3]: Xml сериализация и сохранение совместимости
Здравствуйте, Kore Sar, Вы писали:
ВВ>>При измении типов определяйте для новых типов конверсию, т.е. чтобы MyOldType можно было бы привести к MyNewType. KS>Тут не понял. Какая именно конверсия?
В смысле если тип меняется и становится не совместимым можно попробовать или описать для него преобразования через IConvertible или же вообще сделать враппер который будет отвечать за сериализацию.
Если к примеру вы Boolean поменяли на Enum — тут без враппера реализующего IXmlSerializable ИМХО не обойтись.
ВВ>>Но это в том случае, если использовать стандартную XML-сериализацию.
ВВ>>Я бы в данном случае сделал сериализатор ручками. Причем сериализация бы происходила по манифесту, описанному в том же XML-формате. Манифест бы тупо маппил ХМЛ-элементы на классы, ХМЛ-атрибуты на св-ва и пр. KS>Т.е. рефлексия + парсинг ХМЛ-ки + парсинг манифеста ХМЛ-ного. Можно подумать. Спасибо за идею.
ВВ>>В будущей версии можно было бы к примеру *полностью* переделать всю объектную модель настроек, но написать специальный манифест для совместимости, с помощью которого можно было бы грузить старые конфиги.
ВВ>>Плюс не было бы всего этого уродства связанного со стандартной ХМЛ-сериализацией — конструкторы с параметрами нельзя, абстрактные классы нельзя и проч. и проч. KS>Конструкторы — это мелочь.
Ну если так судить, то все мелочь. Только в результате объектная модель в какой-то месс превращается.
И потом, а вдруг — если вы так уж любите перестраховываться — "завтра" вам потребуется один и тот же класс сериализовать в разный ХМЛ?
KS>Абстрактные можно. XmlTypeAttribute для этого есть.
Эээ, а как он поможет? Примерчик можно?
KS>Какое еще там уродство есть, кроме уже перечисленного?
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Здравствуйте, Kore Sar, Вы писали:
ВВ>>>При измении типов определяйте для новых типов конверсию, т.е. чтобы MyOldType можно было бы привести к MyNewType. KS>>Тут не понял. Какая именно конверсия?
ВВ>В смысле если тип меняется и становится не совместимым можно попробовать или описать для него преобразования через IConvertible или же вообще сделать враппер который будет отвечать за сериализацию. ВВ>Если к примеру вы Boolean поменяли на Enum — тут без враппера реализующего IXmlSerializable ИМХО не обойтись.
Тут рядом посоветовали просто сделать новый проперти и у старого переопределить сеттер, поставив старого в XmlIgnore.
Про IXmlSerializable не понял.
ВВ>Ну если так судить, то все мелочь. Только в результате объектная модель в какой-то месс превращается. ВВ>И потом, а вдруг — если вы так уж любите перестраховываться — "завтра" вам потребуется один и тот же класс сериализовать в разный ХМЛ?
KS>>Абстрактные можно. XmlTypeAttribute для этого есть.
ВВ>Эээ, а как он поможет? Примерчик можно?
Можно, только я ошибся. Не XmlType, а XmlInclude.
[XmlInclude(typeof(Derived))]
abstract class Base
{
}
class Derived : Base
{
}
List<Base> collection легко и без проблем сериализуется.
Здравствуйте, Kore Sar, Вы писали:
KS>Тут рядом посоветовали просто сделать новый проперти и у старого переопределить сеттер, поставив старого в XmlIgnore.
Я ошибся, забыл что атрибуты нельзя в runtime добавлять. А если XmlIgnore проставить, от он и читаться не будет, так что в этом случае надо что-то другое думать.
Re[6]: Xml сериализация и сохранение совместимости
Здравствуйте, MozgC, Вы писали:
MC>Здравствуйте, Kore Sar, Вы писали:
KS>>Тут рядом посоветовали просто сделать новый проперти и у старого переопределить сеттер, поставив старого в XmlIgnore.
MC>Я ошибся, забыл что атрибуты нельзя в runtime добавлять. А если XmlIgnore проставить, от он и читаться не будет, так что в этом случае надо что-то другое думать.
Т.е. у тебя за всё время использования тип пропертей никогда не менялся?
Re[4]: Xml сериализация и сохранение совместимости
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Ну если так судить, то все мелочь. Только в результате объектная модель в какой-то месс превращается.
Никогда не считал классы для Xml сериализации частью объектной модели. Я их воспринимаю как суррогаты для сериализации, не больше. И собственно такой подход позволяет держать один текущий класс рабочий (пусть это будут таки настройки) и толпу XmlSettingsContainerV1, ... XmlSettingsContainerVN, специфичных для разных версий. Каждый из них умеет маппировать версию X в текущий класс настроек. Потому никаких ограничений на изменения между версиями нет. Одна проблема — перед десериализацией узнать, что именно десериализовывать. Но и она решается.
ВВ>И потом, а вдруг — если вы так уж любите перестраховываться — "завтра" вам потребуется один и тот же класс сериализовать в разный ХМЛ?
да, вот так рабочий SettingsContainer можно сериализовать в любой из XmlSettingsContainerVX и обратно. Код преобразования между рабочим классом и суррогатами рукописный.
Re[4]: Xml сериализация и сохранение совместимости
I (stupidly) wrote my own serializer to get around some of these problems. Don't do that; it is a lot of work and you will find subtle bugs in it months down the road.
Т.е. вариант предложеный тобой не подходит (времени у меня мало).
Re[5]: Xml сериализация и сохранение совместимости
Здравствуйте, samius, Вы писали:
ВВ>>И потом, а вдруг — если вы так уж любите перестраховываться — "завтра" вам потребуется один и тот же класс сериализовать в разный ХМЛ? S>да, вот так рабочий SettingsContainer можно сериализовать в любой из XmlSettingsContainerVX и обратно. Код преобразования между рабочим классом и суррогатами рукописный.
Ну и чем это хорошо? Все заканчивается тем, что вы дублицируете объектную модель, фактически у вас получается сериализация Object Model -> Object Model for XML Serialization -> XML. Не проще ли сразу получать XML только более прямым способом? А то это все похоже на какие-то заплатки при работе с не самой лучшей библиотекой.
Re[4]: Xml сериализация и сохранение совместимости
Здравствуйте, Воронков Василий, Вы писали:
ВВ>В смысле если тип меняется и становится не совместимым можно попробовать или описать для него преобразования через IConvertible или же вообще сделать враппер который будет отвечать за сериализацию.
А чем поможет IConvertible?
ВВ>Если к примеру вы Boolean поменяли на Enum — тут без враппера реализующего IXmlSerializable ИМХО не обойтись.
Да, возможно в такой ситуации придется использовать wrapper. Хотя на самом деле я думаю что это все не пригодится.
Re[7]: Xml сериализация и сохранение совместимости
Здравствуйте, Kore Sar, Вы писали:
KS>Тут рядом посоветовали просто сделать новый проперти и у старого переопределить сеттер, поставив старого в XmlIgnore. KS>Про IXmlSerializable не понял.
Я имел в виду написать тип обвертку, который сам будет распарсивать старое значение и преобразовывать его в новое. У этого типа определить для удобства оператор привидения к "новому" типу — т.е. парсили булевый, получили энумерацию.
Кстати, вот такой вопрос — а когда приложение читает старый конфиг, оно должно уметь его сохранять в старом же формате или может сохранить уже в новом? ИМХО очень существенный момент.
Re[5]: Xml сериализация и сохранение совместимости
Здравствуйте, samius, Вы писали:
S>Здравствуйте, Воронков Василий, Вы писали:
ВВ>>Ну если так судить, то все мелочь. Только в результате объектная модель в какой-то месс превращается. S>Никогда не считал классы для Xml сериализации частью объектной модели. Я их воспринимаю как суррогаты для сериализации, не больше. И собственно такой подход позволяет держать один текущий класс рабочий (пусть это будут таки настройки) и толпу XmlSettingsContainerV1, ... XmlSettingsContainerVN, специфичных для разных версий. Каждый из них умеет маппировать версию X в текущий класс настроек. Потому никаких ограничений на изменения между версиями нет. Одна проблема — перед десериализацией узнать, что именно десериализовывать. Но и она решается.
ВВ>>И потом, а вдруг — если вы так уж любите перестраховываться — "завтра" вам потребуется один и тот же класс сериализовать в разный ХМЛ? S>да, вот так рабочий SettingsContainer можно сериализовать в любой из XmlSettingsContainerVX и обратно. Код преобразования между рабочим классом и суррогатами рукописный.
Ох как много кода вы предлагаете писать... Куча классов, для каждого преобразование, ... что-то мне такой подход не нравится.
Re[5]: Xml сериализация и сохранение совместимости
Здравствуйте, samius, Вы писали:
S>Здравствуйте, Воронков Василий, Вы писали:
ВВ>>Ну если так судить, то все мелочь. Только в результате объектная модель в какой-то месс превращается. S>Никогда не считал классы для Xml сериализации частью объектной модели. Я их воспринимаю как суррогаты для сериализации, не больше. И собственно такой подход позволяет держать один текущий класс рабочий (пусть это будут таки настройки) и толпу XmlSettingsContainerV1, ... XmlSettingsContainerVN, специфичных для разных версий. Каждый из них умеет маппировать версию X в текущий класс настроек. Потому никаких ограничений на изменения между версиями нет. Одна проблема — перед десериализацией узнать, что именно десериализовывать. Но и она решается.
ВВ>>И потом, а вдруг — если вы так уж любите перестраховываться — "завтра" вам потребуется один и тот же класс сериализовать в разный ХМЛ? S>да, вот так рабочий SettingsContainer можно сериализовать в любой из XmlSettingsContainerVX и обратно. Код преобразования между рабочим классом и суррогатами рукописный.
Угу, как вариант. На самом деле редко придется разные контейнеры разных версий и код преобразования делать. Если вообще когда либо придется. Как я уже написал если свойства просто добавляются/удаляются/переименовываются то все будет работать вообще без каких либо изменений.
Re[6]: Xml сериализация и сохранение совместимости
Здравствуйте, Kore Sar, Вы писали:
KS>Ох как много кода вы предлагаете писать... Куча классов, для каждого преобразование, ... что-то мне такой подход не нравится.
Скорее всего все время будет только 1 класс. Просто в случае ОЧЕНЬ БОЛЬШИХ изменений придется написать контейнер для новой версии и код конвертации.
Re[6]: Xml сериализация и сохранение совместимости
Здравствуйте, Воронков Василий, Вы писали:
ВВ>Здравствуйте, Kore Sar, Вы писали:
KS>>Т.е. вариант предложеный тобой не подходит (времени у меня мало).
ВВ>Ну смотри сам, я всегда сериализацию делал "врукопашную" и проблем с ней не имел.
У меня "врукопашную" бинарная сериализация. Спасибо, я уже настрадался с ней.