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