最近集成一个医院PACS接口时,里面字段太多了,我本来想自己写反射代码生成请求的XML,但后面想想,算了,直接用序列化吧。
这里就对C#中的序列化做个总结,日后再用到也有个参考。
正文
本文介绍了C#中的序列化及4种序列化对象的方法(BinaryFormatter、SoapFormatter、XmlSerializer、JsonSerializer)。
本文只做了入门级的总结 ,如果需深入了解这几种序列化的方法,可以访问MSDN上的文档。
说明:目前流行的序列化方案是Json和Protobuf。
其中Json可以使用内置的System.Text.Json库也可以使用Newtonsoft.Json库。
关于Protobuf的序列化,后面出一篇文章单独介绍
注意:自.NET 5起,已经不推荐使用BinaryFormatter和SoapFormatter了。
https://learn.microsoft.com/zh-cn/dotnet/standard/serialization/binaryformatter-security-guide#binaryformatter-security-vulnerabilities
对象序列化
序列化是指持久化一个对象的状态到流(如文件流和内存流)的过程。被持久化的数据次序包括以后所有需要用来重建(即反序列化)对象状态所必需的信息。使用序列化,用最小花费来保存各种格式数据就变得轻而易举了。
对象图的作用
当一个对象被持久化到流时,所有的相关数据(基类、包含的对象等)也会被自动序列化,因此,假设我们想持久化一个子类,那么继承链接(即父类、子类的子类)上的所有数据都会被包括进来,也就是对象图。
对象图使用箭头来表示“需要”和“依赖”的关系。
举一个简单的例子,假设有以下的数据模型定义
public class Car { protected Radio Radio { get; set; } }
public class Radio {
}
public class JamesBondCar : Car {
}
有一个基类Car,它配有(has a )Radio。JamsBondCar类扩展Car基类。下图显示了一个模拟这些关系可能的对象图
Car类引用了Radio类(有has-a关系),JamesBondCar类引用了Car类(是is-a关系),也引用了Radio类(因为它继承了这个受保护的成员变量)
可以用下面简单的公式来描述
[Car3 , ref 2] =》 对象3(Car类)依赖于对象2(Radio类)
[Radio 2] =》 对象2(Radio类)不依赖其它
[JamesBondCar 1, ref 3, ref 2] =》 对象1(JamesBondCar类)依赖于对象3和对象2。
因此在序列化或反序列化JamesBondCar类的实例时,对象图确认了Radio类型和Car类型也参与了这个过程。
定义可序列化的对象
使用[Serializable]特性可以标记为可序列化对象。
使用[NonSerialized]特性可以标记成员不会被序列化
public class Student { private int id;
private string name;
private string remark;
public int Id { get => id; set => id = value; } public string Name { get => name; set => name = value; } public string Remark { get => remark; set => remark = value; } }
值得注意的是,[Serializable]特性不会被继承,因此子类也需要标记为[Serializable]
对于XmlSerializer和JsonSerializer,可以不用[Serializable]标记也能正常执行。
对于BinaryFormatter或SoapFormatter序列化未使用[Serializable]标记的类时,会抛出SerializationException异常。
BinaryFormatter
BinaryFormatter在序列化对象时,会持久化每个类型的完全限定名称和定义程序集的完整名称。也叫类型保真(type fidelity)
这种方式有利也有弊。优点就是,使用BinaryFormatter序列化出来的文件可以移植到其它设备,并完整地恢复到对象的原始状态。缺点就是:因为这些类型都是.NET里面定义的,所以只能供.NET使用,其它编程语言无法使用。
使用BinaryFormatter进行序列化的方法如下:
这里以前面的Student类为例
public static void Main(){ Student student = new Student(); student.Id = 1; student.Name = "Name"; student.Remark = "Remard"; SaveAsBinaryFile(student, "D:\\student.dat");}
static void SaveAsBinaryFile(object obj,string filePath){ BinaryFormatter binaryFormatter = new BinaryFormatter(); using(FileStream fs = new FileStream(filePath,FileMode.Create)) { binaryFormatter.Serialize(fs, obj); }
}
序列化保存出来的文件,使用16进制编辑器打开如下
可见BinaryFormatter.Serialize()是一个负责生成对象图,并将字节顺序移动到流的方法。
这里的流可以是其它类型的流,如内存流,网络流等。示例中使用了文件流,直接保存到了文件。
使用BinaryFormatter进行反序列化的方法如下:
static object BinaryFileToObject(string filePath) { BinaryFormatter binaryFormatter = new BinaryFormatter(); using(FileStream fs = new FileStream(filePath,FileMode.Open)) { return binaryFormatter.Deserialize(fs); } }
1 var stu = (Student)BinaryFileToObject(file);2 Console.WriteLine(stu.Id);
SoapFormatter
SoapFormatter类型把对象持久化为一个SAOP消息。
SoapFormatter通过使用XML命名空间来持久化原始程序集的跟踪,它不是类型保真的。
SoapFormatter进行序列化的方法如下:
说明:需要引用 System.Runtime.Serialization.Formattters.Soap.dll
var file = "D:\\soap.soap";Student student = new Student();student.Id = 1;student.Name = "Name";student.Remark = "Remard";SaveAsSOAPFile(student, file);
static void SaveAsSOAPFile(object obj, string filePath) { SoapFormatter soapFormatter = new SoapFormatter(); using (FileStream fs = new FileStream(filePath, FileMode.Create)) { soapFormatter.Serialize(fs, obj); } }
序列化出来的结果如下:
SoapFormatter进行反序列化的方法如下:
static object SOAPFileToObject(string filePath){ SoapFormatter soapFormatter = new SoapFormatter(); using (FileStream fs = new FileStream(filePath, FileMode.Open)) { return soapFormatter.Deserialize(fs); }}
1 var stu = (Student)SOAPFileToObject(file);2 Console.WriteLine(stu.Id);
XmlSerializer
使用XmlSerializer可以将对象的所有公有字段/属性持久化为XML元素。
使用XmlSerializer进行序列化的方法如下:
static void Main(string[] args){ Student student = new Student(); student.Id = 1; student.Name = "Name"; student.Remark = "Remark"; SaveAsXmlFile(student, "D:\\output.xml");}
static void SaveAsXmlFile(object obj,string filePath){ XmlSerializer xmlSerializer = new XmlSerializer(obj.GetType()); using(FileStream fs = new FileStream(filePath, FileMode.Create)) { xmlSerializer.Serialize(fs, obj); }}
序列化出来的结果如下:
使用XmlSerializer进行反序列化的方法如下:
static T XmlFileToObject<T>(string filePath){ XmlSerializer xmlSerializer = new XmlSerializer(typeof(T)); using (FileStream fs = new FileStream(filePath, FileMode.Open)) { return (T)xmlSerializer.Deserialize(fs); }}
Student stu = XmlFileToObject<Student>("D:\\output.xml");Console.WriteLine(stu.Name);
使用XmlSerializer序列化到字符串
public string XmlSerializeToString(object obj){ XmlSerializer xmlSerializer = new XmlSerializer(obj.GetType()); StringBuilder stringBuilder = new StringBuilder(); var emptyNamespaces = new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty }); var settings = new XmlWriterSettings(); settings.Indent = true; settings.OmitXmlDeclaration = true; using (XmlWriter xmlWriter = XmlWriter.Create(stringBuilder,settings)) { xmlSerializer.Serialize(xmlWriter, obj,emptyNamespaces); return stringBuilder.ToString(); }}
在使用XmlSerializer进行序列化时,可以控制 XML数据的生成,这里放到下一遍文章中来进行讲解。
JsonSerializer
使用JsonSerializer可以将对象持久化成json字符串。这对于调用WebApi时非常有用。
System.Text.JsonSerializer并不是.Net Framework内置的库,而是伴随.NET Core 3.0一起发布的一套扩展库。
它支持
- .NET Standard 2.0 and later
- .NET Framework 4.6.2 and later
使用Nuget 搜索安装 System.Text.Json进行安装
使用JsonSerializer进行序列化的方法如下:
static void Main(string[] args){ Student student = new Student(); student.Id = 1; student.Name = "Name"; student.Remark = "Remark";
var json = ObjectConvertToJson(student); Console.WriteLine(json);}
static string ObjectConvertToJson(object obj){ var json = JsonSerializer.Serialize(obj); return json;}
运行结果如下:
使用JsonSerializer进行反序列化的方法如下:
static T JsonToObject<T>(string json){ return JsonSerializer.Deserialize<T>(json);}
var stu = JsonToObject<Student>(json);Console.WriteLine(stu.Name);
参考资料:
.NET序列化
https://learn.microsoft.com/en-us/dotnet/standard/serialization/
XML序列化
https://learn.microsoft.com/en-us/dotnet/standard/serialization/introducing-xml-serialization
Json序列化
https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json/overview
该文章在 2026/1/16 10:07:03 编辑过