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

时间:2011-06-16 15:29:10

标签: exception types versioning deserialization .net-assembly

我终于决定发布我的问题了,花了几个小时在网上搜索解决方案并尝试了一些。

[问题背景]

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

  • XML Importer工具:它的作用是加载/读取一个xml文件,以填充一些数据结构,然后将其序列化为二进制文件。
  • 最终用户应用程序:它将加载由XML Importer生成的二进制文件,并对恢复的数据结构执行一些操作。

目前,我只使用XML Importer用于两种目的(这意味着我首先加载xml并将其保存到二进制文件,然后我重新打开XML Importer并加载我的二进制文件)。

[实际问题]

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

所以我要做的是:

  • 构建XML导入程序的版本
  • 打开XML Importer,加载XML文件并将生成的数据结构保存到二进制文件
  • 重建XML导入程序
  • 打开新构建的XML Importer,加载以前创建的二进制文件并恢复我的数据结构。

此时,我得到一个例外:

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)

为了您的信息(不知道是否有用),它正在努力反序列化的实际类型是List,Grid是一个自定义类(可以正确序列化,因为我能够在使用时执行此操作)相同版本的XML Importer)。

[潜在解决方案]

我相信它来自大会周围的某个地方,因为我阅读了很多关于此的帖子和文章。但是,我已经有一个自定义Binder来处理程序集名称的差异,如下所示:

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

你们中的任何人都知道如何解决这个问题吗?会很棒,请相当:)

3 个答案:

答案 0 :(得分:1)

将工作位移动到单独的程序集,并在“server”和“client”中使用程序集。根据您对问题的解释,如果这是核心问题,这应该解决“错误版本”问题。我还会将任何“模型”(即像Grid这样的状态位)带到域模型项目中,并在两个地方使用它。

答案 1 :(得分:1)

当我遇到同样的问题时,我刚碰到你的线程。特别是使用SerializationBinder的代码示例对我帮助很大。我只需稍微修改它来区分我自己的程序集和Microsoft的程序集。希望它仍然可以帮助你:

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

答案 2 :(得分:0)

我认为问题在于你要告诉它寻找List<>在执行程序集中,而实际上它在系统程序集中。如果原始程序集是您的程序集之一,则只应在程序集中重新分配程序集名称。

此外,您可能必须通过解析类型名称并确保在返回参数化泛型类型时参数类型不是特定于外部程序集来处理绑定器中的泛型参数类型。