使用早期版本反序列化二进制格式化程序文件会产生序列化异常



我在反序列化由我的程序的更高版本序列化生成的中等复杂对象时遇到问题。我得到一个例外:

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:UsersandrewDocumentsVisual Studio 2013ProjectsvtsCSV1 ApplicationTestV1Part2TestV1Part2.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

但是,正如此处所暗示的:反序列化向后兼容注释,

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

健康数据仅定义为:

[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. 这篇文章:反序列化向后兼容性建议 proto-buf.net 作为替代方案。 这会解决我在这里概述的问题吗? 它有局限性吗?

我的实际类结构比Microsoft示例复杂得多,但我确实有数组和列表<>类,所以这是首先考虑的好事。 但是,可能还有其他简单的Microsoft示例所没有的"陷阱"。

任何想法或帮助将不胜感激。

戴夫

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

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

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

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

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

当它看到数组时,它不能立即修复它,因此它会创建一个虚拟引用,然后稍后修复数组

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

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

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

  1. 使用序列化绑定程序

  2. 使用蚕虫代理

  3. 使用等值化。适用于无法更改旧代码且在旧代码中未受控序列化过程的情况

您可以使用简单的解决方案,但这并不好。实现 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));
    }
}

最新更新