C#中泛型集合List反序列化问题及解决方法

时间:2021-10-24 19:42:49

一、一般类型的反序列化程序集问题及处理方法

         在一些应用系统中常常有两个子系统软件A与B:A软件序列化一个数据文件,该文件将在B软件中使用。例如,在15年的交通运输部小样本调查数据的审核软件中,A软件就是笔者自己用的审核规则编制软件;B软件则是给用户使用的审核小样本调查数据的客户端软件,该软件只需要使用A软件发布的规则即可。笔者的处理方法是,A软件序列化审核规则DataTable到一个文件中,B软件反序列化该文件后直接使用。

        一般情况下,A软件序列化类型对象时,将写入类型的名称,且该名称包含了程序集的名字(注意,这个是关键),并且B软件通常不与A软件的程序集名相同。因此,在反序列化时将抛出:“处理的异常:  System.Runtime.Serialization.SerializationException: 无法找到程序集“ConsoleApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null”。这里的异常信息中的ConsoleApplication1就是A软件的程序集名。这类异常的解决方法是:在反序列化时,更改类型查找的程序集名。具体代码如下:

using (FileStream fs = new FileStream("student.dat", FileMode.Open, FileAccess.Read))
{
BinaryFormatter bf = new BinaryFormatter();
bf.Binder = new LocalizedTypeGetter(); // 保证在当前程序集中获取反序列化的类型
Students newObj = (Students)bf.Deserialize(fs); // 这里假设 Students 是一个普通类型
Console.WriteLine(newObj.Name);
}

public class LocalizedTypeGetter : System.Runtime.Serialization.SerializationBinder
{
public override Type BindToType(string assemblyName, string typeName)
{
assemblyName = System.Reflection.Assembly.GetExecutingAssembly().FullName; // 当前程序集
return Type.GetType(String.Format("{0}, {1}", typeName, assemblyName)); // 从当前程序集中获取类型名,注意,类型的名称空间要相同
}
}
        在反序列化时,给序列化类的Binder方法一个自定义的类型LocalizerTypeGetter,该类型重写了BindToType()方法,保证在当前程序集中获取类型。

        这里需要强调的是,反序列化和序列化程序集应该是相同的名称空间(不同的名称空间是否可行?笔者尚未做相关测试,因为笔者的AB软件的名称空间是相同的)。

二、泛型集合List<T>的反序列化问题及处理方法

        前面介绍的方法不能解决泛型集合List<T>的反序列化问题。因为List<T>被.NET框架认为是标准类型,将在mscorlib动态库中查找类型,而不是在用户程序集中查找类型,笔者通过调试跟踪BindToType()方法发现了这个现象。在尚未找到通用方式前,笔者采取了如下“笨”的直接方法解决:

public class LocalizedTypeGetterEx : LocalizedTypeGetter
{
public override Type BindToType(string assemblyName, string typeName)
{
if (typeName.Contains("List") == true && typeName.Contains(typeof(Student).Name) == true) // 如果是泛型集合
{
return typeof(List<Student>); // 直接返回反序列化的类型
}

return base.BindToType(assemblyName, typeName);
}
}

        派生LocalizedTypeGetter类型,再次重写BindToType()方法。在再重写方法中,判断是否是反序列化需要的泛型集合,如果是则直接返回泛型结合的类型List<T>(如代码中的List<Student>)。此时,只需要在反序列化时修改代码为 bf.Binder = new LocalizedTypeGetterEx()即可。

        上述代码在Win7、Visual Studio 2012、.NET Framework3.5环境下调试通过。

         补充:在设计15年小样本调查数据处理软件时,使用了DataTable保存审核规则,反序列化和序列化只需要考虑上面一般类型的反序列化方法即可。然而,最近在做公路养护报表审核软件时,使用泛型List<T>保存审核规则记录,在序列化和反序列化时就碰到了反序列化失败的异常。暂时找到的方法是笨方法,希望以后改进为通用方法。