使用早期版本反序列化BinaryFormatter文件会产生SerializationException。

时间:2016-02-11 22:17:03

标签: .net serialization deserialization binaryformatter

我无法从我的程序的更高版本反序列化序列化产生的中等复杂对象。我得到例外:

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()
...

这与在旧版本的更高版本中尝试反序列化已更改的对象(添加的成员)有关。但是,微软声称它应该可以工作,因为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.

- 使事情不相容并不需要太多。我使用了Microsoft的VTS示例(如上所列)并将以下内容添加到" Person"下的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
}

这足以得到可怕的异常&#34; ...无效的修正数......&#34;。

奇怪的是,如果我只添加一个整体列表,一切都很好。我的问题是:

  1. 导致反序列化失败的原因是什么?在事情失败之前,阶级结构有多复杂?显然,拥有一个具有用户定义类的对象列表的类就足够了。还有什么?
  2. 我该如何解决问题?有办法吗?很高兴知道我们可以添加新成员并能够使用旧版本的软件读取新的序列化文件。
  3. 这篇文章:Deserialization backwards compatibility建议将proto-buf.net作为替代方案。这会处理我在这里概述的问题吗?它有限制吗?
  4. 我的实际类结构比Microsoft示例复杂得多,但我确实有阵列和列表&lt;&gt;所以这是首先要考虑的好事。但可能还有其他的&#34;陷阱&#34;简单的微软例子没有。

    非常感谢任何想法或帮助。

    戴夫

1 个答案:

答案 0 :(得分:2)

我最近遇到了类似的问题,我找不到关于这些“陷阱”的提及

反序列化失败的原因如下:

ObjectManager具有不同的逻辑来解析数组以及引用和值类型的依赖关系。

您添加了一个新的引用类型数组,它在程序集中不存在(您将其标记为第二个版本)

当ObjectManager尝试解析依赖关系时,它会构建图形

当它看到数组时,它无法立即修复它,因此它会创建一个虚拟引用,然后再修复该数组

由于此类型不在程序集中,因此无法解析依赖项。 出于某种原因,它不会从修复的元素列表中删除该数组,最后它会抛出异常“IncorrectNumberOfFixups”

出于某种原因,它仅针对新引用类型的数组抛出异常,因此如果您使用新结构而不是类的集合,一切都将正常工作

控制序列化过程有三种方法,具体取决于您如何更改旧代码:

  1. 使用序列化活页夹

  2. 使用Serizaliation Surrogates

  3. 使用ISerialization。适用于您无法更改旧代码和旧代码的情况,您尚未进行受控序列化过程

  4. 您可以使用简单的解决方案,但这并不好。 实现ISerializable并将您的新类数组编译为类数组,但仅作为数组序列,在数组之外

    因此它将适用于旧版本的程序

    有用的说明:

    此外,您可以通过使用第二种方法来解决这个问题,只需使用结构数组或使用Dictionary,因此作为Dictionary中的每个元素,它就是结构

    您可以做的一个例子: 我不推荐使用此方法,但使用此方法,您将能够保持向后兼容性

    [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));
        }
    }