Совместимость десериализации
Я пытаюсь десериализовать "SomeClass" с более старой версией приложения. Я получаю это ниже исключения
System.Runtime.Serialization.SerializationException: ObjectManager обнаружил недопустимое количество исправлений. Это обычно указывает на проблему в Formatter.
Отсериализация выдает исключение, когда я сериализует версию 0,9 и пытается десериализоваться с использованием версии 0.8. Я думал, что атрибут OptionalField
выполнит трюк, но это не так.
// Version 0.8
[Serializable()]
class Foo{
Bar b;
}
// Version 0.9
[Serializable()]
class Foo{
Bar b;
[OptionalField]
Zoo z;
}
Учитывая, что я не могу изменить версию 0.8, как мне добавить большее состояние в объект Foo, чтобы предыдущие версии могли десериализовать все, что они могут?
Любой указатель будет действительно оценен.
Обновление 1
Бар и зоопарк - это другие классы, которые являются сериализуемыми и содержат Hashtables и другие сериализуемые материалы. В этих классах все сериализуется.
Кроме того, у меня нет никаких расположений.
Ответы
Ответ 1
Во-первых, никогда НИКОГДА не используйте функции сериализации CLR для всего, что напоминает долговременное хранение. Мы делаем эту ошибку обычно один раз, ставим объекты в поле базы данных blob и поглаживаем себя в спину, думая, что мы умны. И тогда CLR получает патч, или наши сборки меняют версии, и вы ввернуты. Так что не делайте этого.
Если вы все еще хотите это сделать, лучший способ решить проблему - создать свой собственный SerializationBinder
, который выглядит примерно так:
public sealed class CustomBinder : SerializationBinder {
public override Type BindToType(string assemblyName, string typeName) {
Type typeToDeserialize = null;
if (typeName.IndexOf("SomeType") != -1) {
typeToDeserialize = typeof(Foo.Bar.Bax.NewType);
}
else if (typeName.IndexOf("SomeOtherType") != -1) {
typeToDeserialize = typeof(Foo.Bar.Bax.SomeOtherNewType);
}
else {
// ... etc
}
return typeToDeserialize;
}
}
Задайте свойство Binder
используемого форматирования перед десериализацией, чтобы он переопределял значения по умолчанию.
Обратите внимание, что здесь я не предлагаю выпадающее решение, я рекомендую, как решить проблему. После того, как вы преобразитесь из того, что вы делаете, изучите другие технологии сериализации, такие как protobuf, или напишите сами. В любом случае вы никогда не должны полагаться на CLR для долгосрочной поддержки сериализации.
Ответ 2
Если конструкторы для каждой версии совместимы (например, есть конструктор без параметров или Foo(Bar b)
для обеих версий), вы можете вызвать
BinaryFormatter formatter = new BinaryFormatter();
formatter.AssemblyFormat = Formatters.FormatterAssemblyStyle.Simple;
Перед десериализацией потока.
Ответ 3
В качестве консультанта для людей, исследующих этот вопрос "пока не стало слишком поздно"... Я настоятельно рекомендую не настаивать на использовании BinaryFormatter. Это нормально для переходной передачи между двумя доменами приложения, которые находятся в синхронизации, но это касается IMO. Существуют другие инструменты сериализации, которые не имеют этих проблем. В терминах двоичных файлов protobuf-net - довольно разумный вариант - позволяющий добавлять/удалять/переименовывать и т.д. Без боли.
Ответ 4
Кажется, что одним из способов сделать это будет иметь объект с версией, таким образом вы можете попробовать десериализовать объект, используя последнюю версию. Если это не сработало, отпустите версию до тех пор, пока она не будет успешной. Затем, как только у вас есть объект, обновите его до последней версии объекта и используйте значения по умолчанию для любых полей, для которых у вас нет данных.
Ответ 5
Атрибут optional field
должен был сделать трюк. Можете ли вы опубликовать фактические классы, которые вы пытаетесь сериализовать.
Сначала вы можете попробовать эти вещи -
конвертировать structs
, если таковые имеются, в classes
попробуйте Soap Serialization
вместо binary serilization