序列化又称串行化,是.NET运行时环境用来支持用户定义类型的流化的机制。其目的是以某种存储形成使自定义对象持久化,或者将这种对象从一个地方传输到另一个地方。
.NET框架提供了两种串行化的方式:1、是使用BinaryFormatter进行串行化;2、使用SoapFormatter进行串行化;3、使用XmlSerializer进行串行化。第一种方式提供了一个简单的二进制数据流以及某些附加的类型信息,而第二种将数据流格式化为XML存储;第三种其实和第二种差不多也是XML的格式存储,只不过比第二种的XML格式要简化很多(去掉了SOAP特有的额外信息)。
可以使用[Serializable]属性将类标志为可序列化的。如果某个类的元素不想被序列化,1、2可以使用[NonSerialized]属性来标志,2、可以使用[XmlIgnore]来标志。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; using System.IO; namespace IserializableDemo { [Serializable] public class MyItemType : ISerializable { public MyItemType() { } private string myProperty_value; public string MyProperty { get { return myProperty_value; } set { myProperty_value = value; } } public void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("props", myProperty_value, typeof(string)); } public MyItemType(SerializationInfo info, StreamingContext context) { myProperty_value = (String)info.GetValue("props", typeof(string)); } } class Program { static void Main(string[] args) { string fileName = "dataStuff.myData"; IFormatter formatter = new BinaryFormatter(); //Program.SerializeItem(fileName, formatter); Program.DeserializeItem(fileName, formatter); } public static void SerializeItem(string filename, IFormatter formatter) { MyItemType t = new MyItemType(); t.MyProperty = "HelloWorld"; FileStream s = new FileStream(filename, FileMode.CreateNew); formatter.Serialize(s, t); s.Close(); } public static void DeserializeItem(string filename, IFormatter formatter) { FileStream s = new FileStream(filename, FileMode.Open); MyItemType t = (MyItemType)formatter.Deserialize(s); Console.WriteLine(t.MyProperty); } } }
关于ISerializable:
任何可以序列化的类都必须用 SerializableAttribute 进行标记。如果某个类需要控制其序列化进程,它可以实现 ISerializable 接口。 Formatter 在序列化时调用GetObjectData,并使用表示对象所需的全部数据来填充所提供的 SerializationInfo。 Formatter 使用图形中对象的类型来创建 SerializationInfo。需要自己发送代理的对象可以使用 SerializationInfo 上的 FullTypeName 和 AssemblyName 方法来更改所传输的信息。
在类继承的情况下,可以序列化从实现 ISerializable 的基类中派生的类。这种情况下,派生的类应在 GetObjectData 的实现内调用 GetObjectData 的基类实现。否则,不会序列化来自基类的数据。
ISerializable 接口表示带有 Constructor 签名(SerializationInfo 信息、StreamingContext 上下文)的构造函数。在反序列化时,仅在格式化程序已反序列化SerializationInfo 中的数据后才调用当前构造函数。一般而言,如果该类未密封,则应保护此构造函数。
无法保证对象被反序列化的顺序。例如,如果一种类型引用尚未反序列化的类型,则会引发异常。如果创建具有这种依赖关系的类型,可以通过实现IDeserializationCallback 接口和 OnDeserialization 方法来解决该问题。
序列化结构处理像处理扩展 Object 的类型一样,处理扩展 MarshalByRefObject 的对象类型。这些类型都可以使用 SerializableAttribute 来标记,并且可以将ISerializable 接口实现为其他任何对象类型。它们的对象状态将被捕获并在流中持续。
当通过 System.Runtime.Remoting 使用这些类型时,远程处理结构会提供一个代理项,它将取代常用的序列化,而将代理序列化为 MarshalByRefObject。代理项是知道如何将特定类型的对象序列化和反序列化的帮助器。代理在大多数情况下对于用户不可见,其类型将是 ObjRef。
作为一种常规的设计模式,类很少会既使用可序列化特性来标记,又扩展 MarshalByRefObject。当组合这两项特性时,开发人员应仔细考虑可能的序列化和远程处理方案。 MemoryStream 就是一个适用的示例。当 MemoryStream ( Stream ) 的基类从 MarshalByRefObject 扩展时,可以捕获 MemoryStream 的状态并随时将其还原。因此,这样做可能是有意义的:将该流的状态序列化到数据库中,并在稍后某一时间将其还原。但是,当通过远程处理来使用时,这种类型的对象将设置代理。
2、使用SoapFormatter进行串行化
和BinaryFormatter类似,我们只需要做一下简单修改即可:
a.将using语句中的.Formatter.Binary改为.Formatter.Soap;
b.将所有的BinaryFormatter替换为SoapFormatter.
c.确保报存文件的扩展名为.xml
经过上面简单改动,即可实现SoapFormatter的串行化,这时候产生的文件就是一个xml格式的文件。
3、使用XmlSerializer进行串行化
关于格式化器还有一个问题,假设我们需要XML,但是不想要SOAP特有的额外信息,那么我们应该怎么办呢?有两中方案:要么编写一个实现IFormatter接口的类,采用的方式类似于SoapFormatter类,但是没有你不需要的信息;要么使用库类XmlSerializer,这个类不使用Serializable属性,但是它提供了类似的功能。
如果我们不想使用主流的串行化机制,而想使用XmlSeralizer进行串行化我们需要做一下修改:
a.添加System.Xml.Serialization命名空间。
b.Serializable和NoSerialized属性将被忽略,而是使用XmlIgnore属性,它的行为与NoSerialized类似。
c.XmlSeralizer要求类有个默认的构造器,这个条件可能已经满足了。
下面看示例:
要序列化的类:
using System; using System.Data; using System.Configuration; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; using System.Xml.Serialization; [Serializable] public class Person { private string name; public string Name { get { return name; } set { name = value; } } public string Sex; public int Age = 31; public Course[] Courses; public Person() { } public Person(string Name) { name = Name; Sex = "男"; } } [Serializable] public class Course { public string Name; [XmlIgnore] public string Description; public Course() { } public Course(string name, string description) { Name = name; Description = description; } }
序列化和反序列化方法:
public void XMLSerialize() { Person c = new Person("cyj"); c.Courses = new Course[2]; c.Courses[0] = new Course("英语", "交流工具"); c.Courses[1] = new Course("数学","自然科学"); XmlSerializer xs = new XmlSerializer(typeof(Person)); Stream stream = new FileStream("c:\\cyj.XML",FileMode.Create,FileAccess.Write,FileShare.Read); xs.Serialize(stream,c); stream.Close(); } public void XMLDeserialize() { XmlSerializer xs = new XmlSerializer(typeof(Person)); Stream stream = new FileStream("C:\\cyj.XML",FileMode.Open,FileAccess.Read,FileShare.Read); Person p = xs.Deserialize(stream) as Person; Response.Write(p.Name); Response.Write(p.Age.ToString()); Response.Write(p.Courses[0].Name); Response.Write(p.Courses[0].Description); Response.Write(p.Courses[1].Name); Response.Write(p.Courses[1].Description); stream.Close(); } 这里Course类的Description属性值将始终为null,生成的xml文档中也没有该节点,如下图: <?xml version="1.0"?> <Person xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <Sex>男</Sex> <Age>31</Age> <Courses> <Course> <Name>英语</Name> <Description>交流工具</Description> </Course> <Course> <Name>数学</Name> <Description>自然科学</Description> </Course> </Courses> <Name>cyj</Name> </Person>
4、自定义序列化
如果你希望让用户对类进行串行化,但是对数据流的组织方式不完全满意,那么可以通过在自定义类中实现接口来自定义串行化行为。这个接口只有一个方法,GetObjectData. 这个方法用于将对类对象进行串行化所需要的数据填进SerializationInfo对象。你使用的格式化器将构造SerializationInfo对象,然后在串行化时调用GetObjectData. 如果类的父类也实现了ISerializable,那么应该调用GetObjectData的父类实现。
如果你实现了ISerializable,那么还必须提供一个具有特定原型的构造器,这个构造器的参数列表必须与GetObjectData相同。这个构造器应该被声明为私有的或受保护的,以防止粗心的开发人员直接使用它。
示例如下:
实现ISerializable的类:
using System; using System.Data; using System.Configuration; using System.Web; using System.Web.Security; using System.Web.UI; using System.Web.UI.WebControls; using System.Web.UI.WebControls.WebParts; using System.Web.UI.HtmlControls; using System.Runtime.Serialization; using System.Runtime.Serialization.Formatters.Binary; /// <summary> /// Employee 的摘要说明 /// </summary> [Serializable] public class Employee:ISerializable { public int EmpId=100; public string EmpName="刘德华"; [NonSerialized] public string NoSerialString = "NoSerialString-Test"; public Employee() { // // TODO: 在此处添加构造函数逻辑 // } private Employee(SerializationInfo info, StreamingContext ctxt) { EmpId = (int)info.GetValue("EmployeeId", typeof(int)); EmpName = (String)info.GetValue("EmployeeName",typeof(string)); //NoSerialString = (String)info.GetValue("EmployeeString",typeof(string)); } public void GetObjectData(SerializationInfo info, StreamingContext ctxt) { info.AddValue("EmployeeId", EmpId); info.AddValue("EmployeeName", EmpName); //info.AddValue("EmployeeString", NoSerialString); } } 序列化和反序列化方法: public void OtherEmployeeClassTest() { Employee mp = new Employee(); mp.EmpId = 10; mp.EmpName = "邱枫"; mp.NoSerialString = "你好呀"; Stream steam = File.Open("c:\\temp3.dat", FileMode.Create); BinaryFormatter bf = new BinaryFormatter(); Response.Write("Writing Employee Info:"); bf.Serialize(steam,mp); steam.Close(); mp = null; //反序列化 Stream steam2 = File.Open("c:\\temp3.dat", FileMode.Open); BinaryFormatter bf2 = new BinaryFormatter(); Response.Write("Reading Employee Info:"); Employee mp2 = (Employee)bf2.Deserialize(steam2); steam2.Close(); Response.Write(mp2.EmpId); Response.Write(mp2.EmpName); Response.Write(mp2.NoSerialString); }