Перевод Рагимы на новый сериализатор

Как я и писал ранее, я занялся первым реальным применением своей библиотеки сериализации. И это оказалось верным, не во всём библиотека оказалась готовой. Старая строилась полностью на «кастомной сериализации», где всё определяли и выполняли сами сериализуемые классы, своим произвольным кодом. Потому во многих из них было такого наворочено, что в новую модель никак, кроме реализации такой же самой «кастомной сериализации», не впихнешь. У меня неоднократно возникали соблазны все-таки её реализовать, но решил держаться до последнего. :) Не исключено, что когда-нибудь и реализую, но не сейчас. Сейчас же я старался везде искать иные пути.

Итак, перевод наконец завершен, и старая библиотека уже полностью удалена. В Рагиме уже всё работает через новую. И я хочу перечислить те дополнения, что пришлось сделать в библиотеке по ходу этого перевода.

  1. Нормально реализована обновляющая десериализация, которая не создает новых объектов, а лишь обновляет свойства существующих. Кроме того, добавлен атрибут для классов UpdateableOnly, и такие классы могут быть только обновлены, на них даже не генерируются конструкторы.
  2. Атрибут DefaultValue теперь может хранить строковое значение для тех типов, что не могут быть в атрибутах (не константы), но могут быть сконвертированы из строки. В качестве примера, тип Color — это структура, для которой написана сериализация в виде одной строки. И точно в таком же формате можно указать и в DefaultValue.
  3. В качестве побочного решения совсем иной проблемы, был добавлен атрибут MemberOrder. Им можно указать свой собственный порядок (де)сериализации полей/свойств. Но главный бонус оказался в другом: данное значение по умолчанию вычисляется из положения класса в иерархии так, чтобы первыми шли свойства предков, а за ними свойства потомков. До того было наоборот, Reflection выдает сперва свойства самого класса, а лишь за ними свойства предков.
  4. Атрибут Reference был предназначен для разрешения ссылок внутри одного вызова десериализации. Так сказать, внутренних ссылок. А мне еще потребовались «внешние». Точнее, возможность сохранить объект в виде некоторого идентификатора, и по нему же его потом восстановить. А так как оно не зависит от десериализуемых данных, то не нужно откладывать момент разрешения ссылки, а можно сразу же проводить. Потому я сделал отдельную, хоть и похожую на Reference, систему — атрибут KeyedStore и интерфейс IKeyResolver.
  5. Так как библиотека просматривает много всяких разных типов, собирая по ним информацию, но далеко не всем им нужны сгенерированные конструкторы и доступы к свойствам, то пришлось разделить инициализацию TypeInfo на два этапа. Второй этап вызовется только в случае необходимости, и лишь тогда возникнет ошибка, если тип не пригоден для сериализации. Пока же эта дополнительная информация не нужна, TypeInfo пригодна для работы даже по таким типам.
  6. Пришлось чуток поправить сбор информации о свойствах, благодаря чему стало можно перекрывать атрибуты у виртуальных свойств. Например, в потомке указать другое XmlName или DefaultValue какому-либо свойству.
  7. И наконец, последнее. Если в XmlName для свойства со списком/словарем указать null, то в xml не будет тэга для самого списка, только тэги элементов списка. При этом, в данном классе не должно быть никаких других свойств, сохраняемых в тэги (сохраняемые в атрибуты остаются разрешены). Мне показалось это полезным, и очень облегчающим вид некоторых получающихся xml-файлов, и я это сделал. Условие вполне достаточное для исключения конфликтов.

Я недоволен некоторыми именами атрибутов, но пока не могу придумать им лучшего названия. В целом же, библиотека стала чуть более завершенной. Кто-то, возможно, скажет, что слишком навороченной и мудреной, и даже будет со своей стороны прав. :) Но делалось всё это все же под реальные мои нужды, а не просто так.

Была еще дикая попытка обмануть .NET, и сделать атрибут для приватных/защищенных свойств, разрешающий их сериализацию (в то время, как публичным атрибут есть наоборот, запрещающий). :) Но как и следовало ожидать, попытка разбилась об FieldAccessException.