Проблема десериализации с NetDataContractSerializer после рефакторинга кода - PullRequest
5 голосов
/ 16 августа 2011

У меня есть ситуация, когда я сериализую некоторые объекты .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 Ответ

2 голосов
/ 16 августа 2011

Да, вы должны иметь хорошо продуманный путь миграции данных, если вы используете сериализацию.Решение, которое вы упомянули, - это то, что я бы сделал лично, чтобы включить проверку в код, который десериализовал бы.Если обнаружена более старая версия, выполните небольшое преобразование, чтобы оно соответствовало новому формату, и действуйте по мере необходимости.Как только все данные преобразованы, этот код может быть устаревшим в будущем выпуске.

...