序列化是指将对象实例的状态存储到存储媒体的过程。在此过程中,先将对象的公共字段和私有字段以及类的名称(包括类所在的程序集)转换为字节流,然后再把字节流写入数据流。在随后对对象进行反序列化时,将创建出与原对象完全相同的副本。
我们经常需要将对象的字段值保存到磁盘中,并在以后检索此数据。尽管不使用序列化也能完成这项工作,但这种方法通常很繁琐而且容易出错,并且在需要跟踪对象的层次结构时,会变得越来越复杂。可以想象一下编写包含大量对象的大型业务应用程序的情形,程序员不得不为每一个对象编写代码,以便将字段和属性保存至磁盘以及从磁盘还原这些字段和属性。序列化提供了轻松实现这个目标的快捷方法。
.NET公共语言运行时 (CLR) 管理对象在内存中的分布,.NET 框架则通过使用反射提供自动的序列化机制。对象序列化后,类的名称、程序集以及类实例的所有数据成员均被写入存储媒体中。对象通常用成员变量来存储对其他实例的引用。类序列化后,序列化引擎将跟踪所有已序列化的引用对象,以确保同一对象不被序列化多次。.NET 框架所提供的序列化体系结构可以自动正确处理对象图表和循环引用。对对象图表的唯一要求是,由正在进行序列化的对象所引用的所有对象都必须标记为 Serializable(请参阅基本序列化)。否则,当序列化程序试图序列化未标记的对象时将会出现异常。
当反序列化已序列化的类时,将重新创建该类,并自动还原所有数据成员的值。
在C#中常见的序列化的方法主要也有三个:BinaryFormatter、SoapFormatter、XML序列化。本文就通过一个小例子主要说说这三种方法的具体使用和异同点。
新建一个vs2008控制台工程SerializableTest,添加一个Person类,加上[Serializable]使其可以被序列化
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace SerializableTest { [Serializable] public class Person { public string Sno { get; set; } public string Name { get; set; } public string Sex { get; set; } public int Age { get; set; } public string DisplayInfo() { return "我的学号是:" +Sno+ "\n我的名字是:"+Name + "\n我的性别为:"+Sex+"\n我的年龄:"+Age+"\n"; } } }
一、BinaryFormatter序列化方式
1、序列化:新建一个Person对象me,然后将其序列化保存到文件personInfo.txt中]
var me = new Person { Sno = "200719", Name = "yuananyun", Sex="man", Age=22 }; //创建一个格式化程序的实例 IFormatter formatter = new BinaryFormatter(); //创建一个文件流 Stream stream = new FileStream("c:/personInfo.txt", FileMode.OpenOrCreate, FileAccess.Write, FileShare.None); formatter.Serialize(stream, me); stream.Close();
执行以上代码将创建一个personInfo.txt文件,它包含了me对象的程序集信息、类名和字段信息。
2、反序列化:从文件personInfo.txt中还原一个对象
//反序列化 Stream destream = new FileStream("c:/personInfo.txt", FileMode.Open, FileAccess.Read, FileShare.Read); var stillme = (Person)formatter.Deserialize(destream); stream.Close();
整个程序如下:
using System; using System.IO; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; namespace SerializableTest { class Program { static void Main(string[] args) { //创建一个格式化程序的实例 IFormatter formatter = new BinaryFormatter(); Console.WriteLine("对象序列化开始……"); var me = new Person { Sno = "200719", Name = "yuananyun", Sex="man", Age=22 }; //创建一个文件流 Stream stream = new FileStream("c:/personInfo.txt", FileMode.OpenOrCreate, FileAccess.Write, FileShare.None); formatter.Serialize(stream, me); stream.Close(); Console.WriteLine("序列化结束!\n"); Console.WriteLine("反序列化开始……"); //反序列化 Stream destream = new FileStream("c:/personInfo.txt", FileMode.Open, FileAccess.Read, FileShare.Read); var stillme = (Person)formatter.Deserialize(destream); stream.Close(); Console.WriteLine("反序列化结束,输出对象信息……"); Console.WriteLine(stillme.DisplayInfo()); Console.ReadKey(); } } }
运行结果如下:
注意:反序列化还原对象时,并不会调用Person类的构造函数
二、SoapFormatter序列化方式
与BinaryFormatter序列化方式类似,只需要把IFormatter formatter = new BinaryFormatter()改成 IFormatter formatter = new SoapFormatter(),并且引用程序集System.Runtime.Serialization.Formatters.Soap.dll(.net自带的)
using System; using System.IO; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Soap; namespace SerializableTest { class Program { static void Main(string[] args) { //创建一个格式化程序的实例 IFormatter formatter = new SoapFormatter(); Console.WriteLine("对象序列化开始……"); var me = new Person { Sno = "200719", Name = "yuananyun", Sex="man", Age=22 }; //创建一个文件流 Stream stream = new FileStream("c:/personInfo.txt", FileMode.OpenOrCreate, FileAccess.Write, FileShare.None); formatter.Serialize(stream, me); stream.Close(); Console.WriteLine("序列化结束!\n"); Console.WriteLine("反序列化开始……"); //反序列化 Stream destream = new FileStream("c:/personInfo.txt", FileMode.Open, FileAccess.Read, FileShare.Read); var stillme = (Person)formatter.Deserialize(destream); stream.Close(); Console.WriteLine("反序列化结束,输出对象信息……"); Console.WriteLine(stillme.DisplayInfo()); Console.ReadKey(); } } }
结果与第一种方式一样。
序列化之后的文件是Soap格式的文件(简单对象访问协议(Simple Object Access Protocol,SOAP),是一种轻量的、简单的、基于XML的协议,它被设计成在WEB上交换结构化的和固化的信息。 SOAP 可以和现存的许多因特网协议和格式结合使用,包括超文本传输协议(HTTP),简单邮件传输协议(SMTP),多用途网际邮件扩充协议(MIME)。它还支持从消息系统到远程过程调用(RPC)等大量的应用程序。SOAP使用基于XML的数据结构和超文本传输协议(HTTP)的组合定义了一个标准的方法来使用Internet上各种不同操作环境中的分布式对象。),其内容如下:
<SOAP-ENV:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:clr="http://schemas.microsoft.com/soap/encoding/clr/1.0" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<a1:Person id="ref-1" xmlns:a1="http://schemas.microsoft.com/clr/nsassem/SerializableTest/SerializableTest%2C%20Version%3D1.0.0.0%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull">
<_x003C_Sno_x003E_k__BackingField id="ref-3">200719</_x003C_Sno_x003E_k__BackingField>
<_x003C_Name_x003E_k__BackingField id="ref-4">yuananyun</_x003C_Name_x003E_k__BackingField>
<_x003C_Sex_x003E_k__BackingField id="ref-5">man</_x003C_Sex_x003E_k__BackingField>
<_x003C_Age_x003E_k__BackingField>22</_x003C_Age_x003E_k__BackingField>
</a1:Person>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
三、XML序列化方式
using System; using System.IO; using System.Runtime.Serialization; using System.Xml.Serialization; namespace SerializableTest { class Program { static void Main(string[] args) { //创建一个格式化程序的实例 XmlSerializer formatter = new XmlSerializer(typeof(Person)); Console.WriteLine("对象序列化开始……"); var me = new Person { Sno = "200719", Name = "yuananyun", Sex="man", Age=22 }; //创建一个文件流 Stream stream = new FileStream("c:/personInfo.txt", FileMode.OpenOrCreate, FileAccess.Write, FileShare.None); formatter.Serialize(stream, me); stream.Close(); Console.WriteLine("序列化结束!\n"); Console.WriteLine("反序列化开始……"); //反序列化 Stream destream = new FileStream("c:/personInfo.txt", FileMode.Open, FileAccess.Read, FileShare.Read); var stillme = (Person)formatter.Deserialize(destream); stream.Close(); Console.WriteLine("反序列化结束,输出对象信息……"); Console.WriteLine(stillme.DisplayInfo()); Console.ReadKey(); } } }
结果与上述相同,xml序列化之后的文件就是一般的一个xml文件,personInfo.txt内容如下:
<?xml version="1.0"?>
<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Sno>200719</Sno>
<Name>yuananyun</Name>
<Sex>man</Sex>
<Age>22</Age>
</Person>
注意:采用xml序列化的方式只能保存public的字段和可读写的属性,对于private等类型的字段不能进行序列化
下面进行验证
将Person的Name属性改成Private,然后查看生成的personInfo.text,其内容如下:
<?xml version="1.0"?>
<Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Sno>200719</Sno>
<Sex>man</Sex>
<Age>22</Age>
</Person>
可以看到Name属性并没有出现在该文件中,反序列化生成的对象中Name属性值为NULL。
以上对c#序列化和反序列化的三种方式进行了举例说明。当然您也可以决定一个类中那些属性序列化或不序列化,可以通过使用 NonSerialized 属性标记成员变量来防止它们被序列化,具体内容请查阅相关资料。