目前见过的序列化实现方法主要有以下两种
- 使用BinaryFormatter实现序列化和反序列化
- 使用Json实现序列化和反序列化
无论哪种反序列化,最重要的一点是只保存关键数据。对于中间信息,尤其是UI控件等。一律不要去保存。正确的思路是,我们在保存关键信息关闭程序后,下次程序打开加载信息,呼出UI界面时,让UI去从我们加载的内容中重新获取显示的内容。
对于第一种方法,由于目前存在安全隐患。现在已经微软官方已经不太推荐了。但对于.net9.0以下的版本仍然可以使用。参考:BinaryFormatter 迁移指南,并且这个方法在使用的时候比较麻烦,对于程序集的引用是强类型的引用。反序列化时如果程序集引入有问题,就会报错。
BinaryFormatter
使用BinaryFormatter进行序列化,需要配合文件流进行。通过BinaryFormatter将数据写入到指定的文件流中。
此外,对于需要序列化的类需要使用特性标明(Serializable),此外还可以通过特性指定某些属性不被序列化(NonSerialized),以及在指定某些方法在序列化后或序列化时执行(OnSerialized、OnSerializing)。
序列化方法
using (FileStream fs = new FileStream(tempSln, FileMode.Create))
{BinaryFormatter binaryFormatter = new BinaryFormatter();fs.Seek(0, SeekOrigin.Begin);binaryFormatter.Serialize(fs, SysProcessSln.g_VarList);
}
对于反序列化,其实是一样的。就是从对应的文件流中把数据还原成对应的类。
反序列化
//反序列化 数据
using (FileStream fs = new FileStream(filepath, FileMode.Open))
{fs.Seek(0, SeekOrigin.Begin);BinaryFormatter binaryFormatter = new BinaryFormatter();//变量SysProcessSln.g_VarList = (List<DataVar>)binaryFormatter.Deserialize(fs);
}
但使用BinaryFormatter反序列化问题是有的,它在反序列化过程中会严格匹配当前类型以及其所在的程序集(版本号等)。如果匹配不成功就会报异常。 这个问题通常会出现在插件式开发中,程序反序列化不同的插件类时。
对于这个问题提供下面两个解决思路。
- 在反序列化使用的IFormatter 对象加入Binder 属性,使其获取要反序列化的对象所在的程序集。
- 配置加载插件文件夹里的所有dll到程序上下文中,这个方法仅需要修改一下App.config的配置文件。该方法在.NETFramework是有效的.对于.NetCore,我没用过。
配置内容
<runtime><assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1"><publisherPolicy apply="yes"/><probing privatePath="Plugins"/></assemblyBinding>
</runtime>
runtime:用于定义应用程序运行时的行为
assemblyBinding:程序集绑定
publisherPolicy apply="yes":如果dll有最新版本,自动加载最新版本
privatePath="Plugins":表示运行时会在 基目录\Plugins\ 下查找未能在基目录中找到的程序集。
Json的反序列化
这个推荐使用NewtonSoft.Json第三方库去完成对应的序列化和反序列。思路上大体一次,此处仅列举一列我遇到的问题以及解决方法。
序列化时的循环引用问题
接口反序列化为指定类
如果一个接口,被多个类实现了。但我们在序列化时又传入了该接口引用的实例,在反序列化过程中,由于我们需要实例化一个对应属性类型的实例,但接口又不允许实例化,就会产生报错。
使用类型名称标记
如果你需要在反序列化时根据接口类型还原具体实现类型,Newtonsoft.Json 提供了一种策略,可以在序列化过程中保存类型信息。这是通过在序列化时自动保存类型名称来实现的,使用 TypeNameHandling 配置。
解决方案
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;public interface IAnimal
{string Name { get; set; }
}
public class Dog : IAnimal
{public string Name { get; set; }public string Breed { get; set; }
}var animal = new Dog { Name = "Buddy", Breed = "Labrador" };// 配置序列化时保存类型信息
string json = JsonConvert.SerializeObject(animal, new JsonSerializerSettings
{TypeNameHandling = TypeNameHandling.All // 这里使用 TypeNameHandling 来保存类型信息
});
Console.WriteLine(json);
// 反序列化时可以正确恢复类型
IAnimal deserializedAnimal = JsonConvert.DeserializeObject<IAnimal>(json, new JsonSerializerSettings
{TypeNameHandling = TypeNameHandling.All
});
Console.WriteLine(deserializedAnimal.GetType()); // 输出: Dog