2016-02-11 3 views
2

У меня возникли проблемы с десериализацией умеренно сложных объектов, созданных путем сериализации из более поздней версии моей программы. Я получаю исключение:Deserializing BinaryFormatter Файлы с более ранней версией вызывают исключение SerializationException.

System.Runtime.Serialization.SerializationException was unhandled 
Message=The ObjectManager found an invalid number of fixups. This usually  indicates a problem in the Formatter. 
Source=mscorlib 
StackTrace: 
    at System.Runtime.Serialization.ObjectManager.DoFixups() 
    at System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(HeaderHandler handler, __BinaryParser serParser, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage) 
    at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, HeaderHandler handler, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage) 
    at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream) 
    at Microsoft.Samples.TestV1.Main(String[] args) in c:\Users\andrew\Documents\Visual Studio 2013\Projects\vts\CS\V1 Application\TestV1Part2\TestV1Part2.cs:line 29 
    at System.AppDomain._nExecuteAssembly(Assembly assembly, String[] args) 
    at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() 
    at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state) 
    at System.Threading.ThreadHelper.ThreadStart() 
... 

Это связано с попытками десериализации объектов, которые были изменены (добавлены члены) в более поздних версиях с более старой версией. Однако Microsoft заявляет, что она должна работать из-за VTS. См.: https://msdn.microsoft.com/en-us/library/ms229752(v=vs.110).aspx

Они представляют собой пример, который не изменен, действительно позволяет десериализовать более свежие классы с более старой версией. См: https://msdn.microsoft.com/en-us/library/7a6c3wzt(v=vs.110).aspx

Однако, как намекнул здесь: Deserialization backwards compatibility с комментарием,

kareph, what is the real type of Zoo ? I remember some types (arrays) just didn't work right. 

- это не займет много, чтобы сделать вещи несовместимые. Я взял пример VTS от Microsoft (перечисленных выше), и добавил следующее к примеру V2 ApplicationCS под «Человек» класса:

[OptionalField(VersionAdded = 2)] 
    private List<HealthData> _healthDataList; 
public List<HealthData> HealthDataList 
    { 
     get { return _healthDataList; } 
     set { _healthDataList = value; } 
    } 

HealthData просто определяется как:

[Serializable] 
public class HealthData 
{ 
    #region Fields 
    private int _weight; 

    private int _height; 
    #endregion 

    #region Properties 
    public int Weight 
    { 
     get { return _weight; } 
     set { _weight = value; } 
    } 

    public int Height 
    { 
     get { return _height; } 
     set { _height = value; } 
    } 
    #endregion 
} 

Этого достаточно, чтобы получить ужасное исключение "... недопустимое количество исправлений ...".

Любопытно, что если я просто добавлю список целых чисел, все будет хорошо. Я задавал следующие вопросы:

  1. Что вызывает десериализацию? Насколько сложной может быть структура класса до того, как все провалится? Очевидно, что достаточно класса, у которого есть список объектов, которые являются определенными пользователем классами. Что еще?
  2. Как я могу исправить ситуацию? Есть ли способ? Было бы неплохо узнать, что мы можем добавлять новых членов и иметь возможность читать новые сериализованные файлы со старыми копиями программного обеспечения.
  3. Этот пост: Deserialization backwards compatibility предлагает proto-buf.net как альтернативу. Будет ли это устранять проблему, которую я здесь изложил? Имеются ли ограничения?

Моя реальная структура класса совсем немного сложнее, чем, например Microsoft, но я, конечно, есть Массивы и списки <> классов, так что это хорошая вещь, чтобы рассмотреть в первую очередь. Но могут быть и другие «gotchas», которых нет в простом примере Microsoft.

Любые идеи или помощь будут высоко оценены.

Dave

ответ

2

Я недавно столкнулся с подобной проблемой, и я не мог найти упоминание об этих «подводных камнях»

десериализация терпит неудачу по следующей причине:

ObjectManager имеет другую логику для решения зависимостей для массивов и ссылочных и стоимостных типов.

Вы добавили массив нового типа ссылки, который отсутствует в сборке (у вас есть его помечен как вторая версия)

Когда ObjectManager пытается разрешить зависимости он строит граф

Когда он видит массив, он не может исправить его немедленно, так что он создает фиктивную ссылку, а затем исправляет массив позже.

И поскольку этот тип не находится в сборке, зависимости не могут быть разрешены. По какой-то причине, он не удаляет массив из списка элементов для исправления и в конце концов он бросает исключение «IncorrectNumberOfFixups»

По какой-то причине, он вызывает исключение только для массивов новых ссылочных типов , так что если вы используете коллекцию новых структур, а не классов, то все будет работать правильно

есть три способа управления процессом сериализации, в зависимости от того, как вы можете внести изменения в старый код или нет:

  1. Использование Сериализация Binder

  2. Использование Serizaliation Суррогаты

  3. Использование ISerialization. Подходит процесс, если вы не в состоянии внести изменения в старый код и в старом коде, вы не контролировалась сериализации

Вы можете использовать простое решение, но это не хорошо. Реализовать ISerializable и написать, что ваш массив новых классов не сериализовать как массив классов, а просто как последовательность элементов, за пределами массива

Так будет правильно работать на старую версию программы

полезное примечание:

Кроме того, вы можете исправить это с помощью второго метода, просто использовать массив структур или использовать словарь, так как каждый элемент словаря является структура

Пример того, что вы можете сделать: Я не рекомендую этот метод, но с помощью этого метода, вы будете иметь возможность поддерживать обратную совместимость

[Serializable] 
class NewItem 
{ 
    public string Name; 
} 

[Serializable] 
class Item : ISerializable 
{ 
    [OptionalField] 
    private List<NewItem> _newItems; 

    public List<NewItem> NewItems 
    { 
     get { return _newItems; } 
     set { _newItems = value; } 
    } 

    public Item() 
    { 

    } 

    protected Item(SerializationInfo info, StreamingContext context) 
    { 
     var newItemsSize = (int)info.GetValue("_newItemsSize", typeof(int)); 
     _newItems = new List<NewItem>(newItemsSize); 
     for (int i = 0; i < newItemsSize; i++) 
     { 
      var item = (NewItem)info.GetValue($"_newItem{i}", typeof(NewItem)); 
      _newItems.Add(item); 
     } 
    } 

    public void GetObjectData(SerializationInfo info, StreamingContext context) 
    { 
     info.AddValue("_newItemsSize", _newItems.Count, typeof(int)); 
     for (int i = 0; i < _newItems.Count; i++) 
      info.AddValue($"_newItem{i}", _newItems[i], typeof(NewItem)); 
    } 
} 
Смежные вопросы