Проблема десериализации с NetDataContractSerializer после рефакторинга кода
У меня есть ситуация, когда я сериализую некоторые объекты.NET с использованием NetDataContractSerializer и сохраняю XML в базе данных, чтобы запомнить состояние этих объектов в приложении. Недавно я только что столкнулся с первой ситуацией, когда в результате некоторого рефакторинга кода для имен свойств и типов не удалось десериализовать эти данные XML.
До сих пор я придумал два разных плана атаки на то, как бороться с перерывами совместимости версий, такими как эти, которые должны использовать средства, доступные в самом NetDataContractSerializer, для управления десериализацией или просто для непосредственного преобразования XML. Из моих экспериментов и исследований выяснилось, что можно десериализовать в другой тип, используя пользовательский SerializationBinder, и изменения имени / типа свойства могут быть учтены либо путем реализации ISerializable, либо путем написания суррогата сериализации путем реализации ISurrogateSelector и ISerializationSurrogate. К сожалению, этот предпочтительный механизм не сработал, и, если я не смогу показать иначе, кажется, что с помощью суррогатов перемещение между версиями сериализованных данных невозможно с NetDataContractSerializer, и это происходит из-за какого-то необъяснимого дизайнерского решения Microsoft. Microsoft предложила использовать одну и ту же сериализацию с обеих сторон, что полностью противоречит цели использования суррогата для помощи в случаях изменения имени типа или его перемещения в другое пространство имен или сборку.
Чтобы исправить это, используйте тот же экземпляр NetDataContractSerializer или другой экземпляр, который также инициализирован совместимым SurrogateSelector.
Это объяснение вступает в противоречие со статьей MSDN, в которой говорится об использовании пользовательского связывателя для замены типов наряду с другими изменениями в структуре сериализации.
Во время десериализации форматировщик видит, что связыватель был установлен. Поскольку каждый объект собирается десериализоваться, средство форматирования вызывает метод связывателя BindToType, передавая ему имя и тип сборки, которые преобразователь хочет десериализовать. На этом этапе BindToType решает, какой тип должен быть действительно создан, и возвращает этот тип.
Обратите внимание, что исходный тип и новый тип должны иметь одинаковые точные имена и типы полей, если новый тип использует простую сериализацию через настраиваемый атрибут Serializable. Тем не менее, новая версия типа может реализовать интерфейс ISerializable, и тогда будет вызван его специальный конструктор, и тип может проверить значения в объекте SerializationInfo и определить, как десериализовать себя.
Так что я либо смогу заставить NetDataContractSerializer работать на десериализацию моего V1 XML в мои типы V2, либо мне придется вручную преобразовывать XML. Если кто-то может доказать, что SerializationInfo NetDataContractSerializer действительно работает при использовании ISerializable или суррогатах сериализации, которые будут превосходны или, по крайней мере, дадут лучшее объяснение, чем тот, который дан Microsoft, в противном случае я, вероятно, опубликую новый вопрос, чтобы обсудить лучший способ в.NET для преобразования старого XML напрямую.
ОБНОВЛЕНИЕ 2011-08-16: После некоторых экспериментов выясняется, что метод ISerializable и суррогатная сериализация работают нормально, если исходный тип, который был сериализован, реализован ISerializable, иначе, если тип только что использовал атрибут [Serializable], кажется, что каждое поле в объекте В графе отсутствует некоторая ценная информация о типе в виде дополнительных атрибутов.
Пример использования атрибута [Serializable]
<OldClass2 xmlns:i="http://www.w3.org/2001/XMLSchema-instance" z:Id="1" z:Type="NdcsSurrogateTest.OldClass2" z:Assembly="NdcsSurrogateTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" xmlns="http://schemas.datacontract.org/2004/07/NdcsSurrogateTest">
<_stable z:Id="2">Remains the same</_stable>
<_x003C_OldProperty_x003E_k__BackingField>23</_x003C_OldProperty_x003E_k__BackingField>
</OldClass2>
Пример реализации ISerialzable:
<OldClass2 xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns:x="http://www.w3.org/2001/XMLSchema" z:Id="1" z:Type="NdcsSurrogateTest.OldClass2" z:Assembly="NdcsSurrogateTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/" xmlns="http://schemas.datacontract.org/2004/07/NdcsSurrogateTest">
<_stable z:Id="2" z:Type="System.String" z:Assembly="0" xmlns="">Remains the same</_stable>
<_x003C_OldProperty_x003E_k__BackingField z:Id="3" z:Type="System.Int32" z:Assembly="0" xmlns="">23</_x003C_OldProperty_x003E_k__BackingField>
</OldClass2>
При десериализации первого примера с использованием NetDataContractSerializer с настраиваемым связывателем для изменения типа и последующей реализации ISerializable для этого типа или предоставления суррогатного селектора, который назначает суррогат сериализации, который в основном выполняет роль ISerializalbe, тогда вы увидите пустой SerializationInfo в ISerializationSurrogate Метод.SetObjectData. При обработке xml во втором примере SerializationInfo, похоже, получает правильную информацию, и все работает как положено.
Мой вывод заключается в том, что XML-код по умолчанию, созданный NetDataContractSerializer для типов, поддерживающих сериализацию только через SerializableAttribute, не будет совместим с десериализацией с использованием методов ISerializable или суррогатной сериализации из-за отсутствия информации о типе. Таким образом, чтобы сделать использование NetDataContractSerializable более перспективным в будущем, необходимо настроить сериализацию, чтобы эта информация о типе была включена в XML, чтобы впоследствии можно было настроить десериализацию без необходимости преобразования исходного XML-кода вручную.
1 ответ
Да, вы должны иметь хорошо продуманный путь миграции данных, если вы используете сериализацию. Решение, которое вы упомянули, - это то, что я бы сделал лично, чтобы включить проверку в код, который десериализовал бы. Если обнаружена более старая версия, выполните небольшое преобразование, чтобы оно соответствовало новому формату, и действуйте по мере необходимости. Как только все данные преобразованы, этот код может быть устаревшим в будущем выпуске.