Exception 反序列化问题:从其他程序版本反序列化时出错

Exception 反序列化问题:从其他程序版本反序列化时出错,exception,types,versioning,deserialization,.net-assembly,Exception,Types,Versioning,Deserialization,.net Assembly,在花了几个小时在互联网上寻找解决方案并尝试了一些方法之后,我终于决定发布我的问题 [问题背景] 我正在开发一个应用程序,该应用程序将分两部分部署: XML导入工具:它的作用是加载/读取XML文件,以填充一些数据结构,然后将这些数据结构序列化为二进制文件 最终用户应用程序:它将加载XML导入器生成的二进制文件,并对恢复的数据结构进行处理 目前,我仅将XML导入器用于这两个目的(这意味着我首先加载XML并将其保存到二进制文件,然后重新打开XML导入器并加载二进制文件) [实际问题] 这很好,我能

在花了几个小时在互联网上寻找解决方案并尝试了一些方法之后,我终于决定发布我的问题

[问题背景]

我正在开发一个应用程序,该应用程序将分两部分部署:

  • XML导入工具:它的作用是加载/读取XML文件,以填充一些数据结构,然后将这些数据结构序列化为二进制文件
  • 最终用户应用程序:它将加载XML导入器生成的二进制文件,并对恢复的数据结构进行处理
目前,我仅将XML导入器用于这两个目的(这意味着我首先加载XML并将其保存到二进制文件,然后重新打开XML导入器并加载二进制文件)

[实际问题]

这很好,我能够恢复加载XML后的所有数据,,只要我使用相同的XML导入器构建即可。这是不可行的,因为我至少需要两个不同的版本,一个用于XML导入器,另一个用于最终用户应用程序。请注意,我用于测试的XML导入器的两个版本在源代码和数据结构方面是完全相同的,唯一的区别在于构建编号(为了强制不同的构建,我只需在某处添加一个空格,然后再次构建)

所以我想做的是:

  • 构建我的XML导入器的一个版本
  • 打开XML导入器,加载XML文件,并将生成的数据结构保存到二进制文件中
  • 重建XML导入器
  • 打开新构建的XML导入器,加载先前创建的二进制文件并恢复我的数据结构
此时,我得到一个例外:

SerializationException: Could not find type 'System.Collections.Generic.List`1[[Grid, 74b7fa2fcc11e47f8bc966e9110610a6, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]'.
System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadType (System.IO.BinaryReader reader, TypeTag code)
System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadTypeMetadata (System.IO.BinaryReader reader, Boolean isRuntimeObject, Boolean hasTypeInfo)
System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadObjectInstance (System.IO.BinaryReader reader, Boolean isRuntimeObject, Boolean hasTypeInfo, System.Int64& objectId, System.Object& value, System.Runtime.Serialization.SerializationInfo& info)
System.Runtime.Serialization.Formatters.Binary.ObjectReader.ReadObject (BinaryElement element, System.IO.BinaryReader reader, System.Int64& objectId, System.Object& value, System.Runtime.Serialization.SerializationInfo& info)
仅供参考(不知道是否有用),它正在努力反序列化的实际类型是一个列表,网格是一个自定义类(它可以正确序列化,就像我在使用同一版本的XML导入器时所做的那样)

[潜在解决方案]

我确实相信它来自大会的某个地方,因为我读了很多关于这方面的帖子和文章。但是,我已经有了一个定制的活页夹来处理程序集名称的差异,如下所示:

public sealed class VersionDeserializationBinder : SerializationBinder
{ 
    public override Type BindToType( string assemblyName, string typeName )
    { 
        if ( !string.IsNullOrEmpty( assemblyName ) && !string.IsNullOrEmpty( typeName ) )
        { 
            Type typeToDeserialize = null; 
            assemblyName = Assembly.GetExecutingAssembly().FullName; 
            // The following line of code returns the type. 
            typeToDeserialize = Type.GetType( String.Format( "{0}, {1}", typeName, assemblyName ) ); 

            return typeToDeserialize; 
        } 

        return null; 
    }
}
在此反序列化之前,我将其分配给BinaryFormatter:

    public static SaveData Load (string filePath) 
    {
        SaveData data = null;//new SaveData ();
        Stream stream;

        stream = File.Open(filePath, FileMode.Open);


        BinaryFormatter bformatter = new BinaryFormatter();
        bformatter.Binder = new VersionDeserializationBinder(); 
        data = (SaveData)bformatter.Deserialize(stream);
        stream.Close();

        Debug.Log("Binary version loaded from " + filePath);

        return data; 
    }

你们有谁知道我该怎么修吗?太棒了,请注意:)

将工作位移动到一个单独的程序集,并在“服务器”和“客户端”中使用该程序集。根据您对问题的解释,如果“版本错误”是核心问题的话,这应该可以解决这个问题。我也会将任何“模型”(即类似于网格的状态位)带到域模型项目中,并在这两个位置使用它。

我认为问题在于您告诉它在执行程序集中查找列表,而实际上它在系统程序集中。如果原始程序集是您的程序集之一,则只应在活页夹中重新指定程序集名称


此外,您可能需要在绑定器中专门处理泛型的参数类型,方法是解析类型名称,并确保返回参数化泛型类型时参数类型不是外部程序集的特定类型。

我遇到了您的线程,而我也遇到了同样的问题。特别是您的SerializationBinder代码示例对我帮助很大。我只需要稍微修改一下,就可以看出我自己的程序集和微软的程序集之间的区别。希望它也能帮助你:

sealed class VersionDeserializationBinder : SerializationBinder
{
    public override Type BindToType(string assemblyName, string typeName)
    {
        Type typeToDeserialize = null;
        string currentAssemblyInfo = Assembly.GetExecutingAssembly().FullName;

        //my modification
        string currentAssemblyName = currentAssemblyInfo.Split(',')[0];
        if (assemblyName.StartsWith(currentAssemblyName))assemblyName = currentAssemblyInfo;

        typeToDeserialize = Type.GetType(string.Format("{0}, {1}", typeName, assemblyName));
        return typeToDeserialize;
    }
}

是否使用强名称程序集?这可能就是为什么它不能识别出这些类是相同的。老实说,我对程序集一无所知,今天是我第一次不得不绕过它们。我已经看到有很多关于这些的msdn页面,尽管我没有时间阅读它们,因为我在下午晚了一点的时候无意中看到了它们。因此,为了回答您的问题,我没有修改任何有关程序集的内容,也没有设置任何特殊的内容;我不知道默认情况下程序集是否为强名称程序集(在这种情况下,我使用的是这些程序集),也不知道是否为强名称程序集(在这种情况下,我不应该使用强名称程序集)。“将工作位移动到单独的程序集,并在“服务器”和“客户端”中使用该程序集=>如何实现这一点?正如我在上面对antlersoft的回复评论中指出的那样,目前我完全不知道如何处理程序集,对它的用途只有一个非常模糊的概念。明天我回去工作的时候,我肯定会读到这方面的内容,但如果你有一个起点,那就太好了。所以,在下面的antlersoft解释之后,我意识到这确实是一个解决方案。不幸的是,它不适合我,因为我正在使用Unity,而且它使使用库变得更加困难,特别是因为目标应用程序将是跨平台的。我感谢你们两位的帮助,并将研究另一种方法来做我想做的事情,而不使用我想要的工具:/关于第一部分,我知道它为什么会不稳定,尽管我不认为这里是这样,因为我也尝试注释掉重新分配的位,但仍然得到完全相同的输出。现在关于第二部分。。。我不认为我得到了一半:/我对此感到非常抱歉,尽管我昨天才开始使用序列化,在该领域仍然是一个纯N00B。。。(在这个程序集和程序集之间,看起来我一定“不想”理解您的答案>。<尽管我向您保证它们是最受欢迎的!)程序集是.Net可执行文件,可以是.exe或.dll。每个类都属于一个程序集。如果一个类位于不同的程序集中,.Net将倾向于认为它们是不同的类,即使源是相同的。上面的解决方案建议将您的公共类(您序列化的类)放入.dll程序集中(这将是一个