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

标签 .net serialization deserialization binaryformatter

我在反序列化由程序的较新版本的序列化生成的中等复杂对象时遇到问题。我得到一个异常(exception):

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
}

这足以引发可怕的异常“...无效的修复次数...”。

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

  1. 什么原因导致反序列化失败?在事情失败之前,类结构可以有多复杂?显然,拥有一个包含用户定义类的对象列表的类就足够了。还有什么?
  2. 我该如何解决问题?有办法吗?很高兴知道我们可以添加新成员并能够使用旧的软件副本读取新的序列化文件。
  3. 这篇文章:Deserialization backwards compatibility建议使用 proto-buf.net 作为替代方案。这可以解决我在这里概述的问题吗?有限制吗?

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

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

戴夫

最佳答案

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

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

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

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

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

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

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

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

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

  1. 使用序列化绑定(bind)器

  2. 使用序列化代理

  3. 使用 ISerialization。适合如果您无法对旧代码进行更改并且在旧代码中,您尚未受控序列化过程

您可以使用一个简单的解决方案,但这并不好。 实现 ISerialized 并写入新类数组不会序列化为类数组,而只是序列化为数组外部的元素序列

因此它可以在旧版本的程序中正常工作

有用的注释:

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

您可以执行的操作示例: 我不推荐这种方法,但是使用这种方法,您将能够保持向后兼容性

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

关于.net - 使用早期版本反序列化 BinaryFormatter 文件会产生 SerializationException。,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35351196/

相关文章:

c# - 如何使用 Mutex 或 EventWaitHandle 同步 4 个不同的进程

c# - 在 C# Windows 窗体中显示 MDI 子窗体时如何避免闪烁?

php - Dropzone上传图片并将所有图片序列化为一份数据保存

c# - 使用 Message.GetBody<> 时,仅根据请求反序列化根对象

java - 使用 Jackson ObjectMapper 反序列化或序列化任何类型的对象并处理异常

debugging - 预期的行为?除在 Debug模式下外,MATLAB会忽略反序列化时的错误

c# - 如何将一批 .NET DLL 反编译为 Visual Studio 项目

c# - 使用 C# 从流中读取字符串

javascript - 如何序列化Json javascript

java - SnakeYaml - 反序列化为字符串和自定义对象的映射时出现 ClassCastException