序列化定义 序列化是将对象状态转换为可保持或传输的格式的过程。与序列化相对的是反序列化,它将流转换为对象。在此过程中,先将对象的公共字段和私有字段以及类的名称(包括类所在的程序集)转换为字节流,然后再把字节流写入数据流。在随后对对象进行反序列化时,将创建出与原对象完全相同的副本。
序列化种类 .NET框架提供两种格式的序列化,二进制序列化(Binary Serialization)和XML序列化(XML Serialization)。
1. 二进制序列化:将对象分解成为简单的二进制格式,这种类型的序列化并不会遗失原来的类型数据,对象使用二进制序列化分解之后,可以被传送至各种存储媒介,包括数据流,磁盘,内存甚至网络上连接的主机。 2. XML序列化:将对象分解成为XML格式,使用SOAP标准来访问,由于XML与SOAP是开放标准,因此特别适用于对象需要跨越网络传送的情形,与二进制序列化不同的是,使用这种格式的序列化,只会序列化分解类型中的公用(Public)属性和字段等内容。
序列化类的功能 .NET框架针对两种不同格式的序列化技术,均提供了相应的类:BinaryFormatter类以及SoapFormatter ,XmlSerializer类。
BinaryFormatter用于二进制格式序列化的操作,位于如下命名空间中: System.Runtime.Serialization.Formatters.Binary
SoapFormatter提供XML序列化的支持,其所处的命名空间如下: System.Runtime.Serialization.Formatters.Soap
XmlSerializer提供XML序列化支持,所处命名空间为: System.Xml.Serialization
这三种类提供对象序列化分解操作以及还原序列化的相关方法,简化序列化对象所需的实现细节。而要想实际的写出这个流,就要使用那些实现了IFormatter接口的类里的Serialize和Deserialize方法。
1.BinaryFormatter类 用来将对象进行二进制的格式化分解,并将二进制格式化分解的对象组合还原,Serialize() 以及Deserialize() 方法分别用来序列化以及还原序列化。 下面是Serialize() 方法的定义: Public void Serialize(Stream,object); 此方法接受stream类型以及object类型对象参数,将其中object类型对象序列化之后,传递到第1个参数所指定的数据流对象。 下面是还原序列化的方法Deserialize() 的定义,这个方法将上面序列化分解的对象重新组合还原,以取得原始对象: Public object Deserialize(Stream serializationStream); 其中Stream类型参数便是所要还原的数据流,Deserialize方法从这个数据流对象中重新组合还原被序列化分解的对象。
2.SoapFormatter类 用来将对象序列化分解为SOAP格式以及还原序列化对象,这个类同样提供序列化对象以及还原序列化操作的方法,名称同样为Serialize() 以及Deserializ() ,甚至所接受的参数值与上面的BinaryFormatter类也相同。
当要将一个类的实例对象进行序列化分解之前,首先必须要确认这个类是否可以进行序列化分解,一个类通常通过属性 [Serializable] 将其标注为可序列化(XmlSerializer方法可以不标注而直接序列化),换句话说,可以依据所要序列化分解的格式,使用上面任一个类对标注为 [Serializable] 的类实例对象进行序列化分解。当然,在序列化对象时,也可以使用属性 [NonSerialized] ,将对象中的某些成员标注为不可序列化,有选择性的只分解对象中的某些数据成员,那些标注为 [NonSerialized] 的数据成员,在对象的序列化过程中将不会被分解。
3. XmlSerializer类 用来将对象序列化分解为XML格式以及还原序列化对象,实现方法同上。
XmlSerializer支持很多特性(ATTRIBUTES),它们可以用于为特定的类设定序列化方式。例如,可将某个字段或属性标以 [XmlIgnore] 特性,从而将其排除在序列化机制之外。另外 [XmlElement] 可用于指定XML元素名称(这个元素名称被用于特定的属性或字段)。
对于经由SoapFormatter,BinaryFormatter的序列化,也可以使用特性进行某种程度的控制。例如,[NonSerialized] 特性等价于XmlSerializer的 [XmlIgnore] 特性。对序列化过程的终极控制,可以通过在其实休打算被序列化的类上,实现ISerializable接口。XmlSerializer不能用于序列化任何实现了IDictionary接口的类的实体,比方说Hashtable(散列表),而SoapFormatter和BinaryFormatter却没有这个限制。
微软将XmlSerializer用于Web Services,而将BinaryFormatter和SoapFormatter用于远程化。
序列化类使用选择 可以依据具体情况来进行有效。XmlSerializer的限制比较严格,比如,要求目标类有一个无参数的构造器,并且只有公开的读,写属性和字段时才可被序列化。不过,从好的方面说,XmlSerializer对定制XML文档提供了良好的支持。XmlSerializer的特性意味着,它最适合用在跨平台的情况下,或者用于从现有的XML文档构建对象。SoapFormatter和BinaryFormatter比XmlSerializer的限制要少,它们可以序列化私有字段。然而,它们都需要目标类标以 [Serializable] 特性,因此,像XmlSerializer一样,需要小心以序列化方式来编写类。有些诡异的东西也是要小心提防的。在反序列化时,新对象的构造器并不被调用。对SoapFormatter和BinaryFormatter的选择,取决于应用。BinaryFormatter对于序列化和反序列化都是发生在.NET平台上并且执行性能很重要的情况大有用处。通常来说,在所有其它情况下,SoapFormatter更有意义,也更易于调试。
自定义序列化 自定义序列化行为 序列化与还原序列化均由指定的Formetter自动完成。也可以选择实现ISerializable接口,自行创建专属的Formetter,以达到自行控制对象序列化及还原序列化的目的。在某些情形下,我们特别需要序列化的过程,例如若变量值的内容在序列化之后会失去原先存储的值,此时你便可以自定义序列化的行为,强迫恢复原先的内容。你可以在对象序列化的过程中,进一步使用加密技术,改变数据的格式,在反序列化操作的时候,以相对的解密技术将数据还原。自定义序列化的行为允许你设计序列化对象的过程,而不是直接将指定的对象分解然后放入网络或是存储到数据流,避免数据的遗失,甚至在序列化进程发生不当撷取的情形。 自定义序列化方法 实现ISerializable接口可以帮助你自定义对象的序列化行为,继承这个接口的类,必须同时实现方法GetObjectData() 和Constructor构造函数。
GetObjectData() 用来将对象相关信息填入SerializationInfo对象,其定义如下: Void GetObjectData(SerializationInfo info,StreamingContext context); 其中的info是SerializationInfo类型的对象参数。SerializationInfo类被设计用来存储对象自定义序列化操作时所需的信息,context是一种StreamingContext结构类型参数,表示序列化操作的来源数据流。
除了实现GetObjectData() 方法外,自定义序列化的过程,还必须完成特定构造函数的实现,构造函数在对象还原序列化时,将上面的info 参数内容传递到类。实现构造函数,必须引用SerializationInfo所定义的实例方法GetValue() ,提供指定的成员变量值。其特定构造函数形式如下: Public 类名(SerializationInfo info,StreamingContext context);
ISerializable接口位于System.Runtime.Serialization命名空间,在实现接口之前,必须引用这个命名空间。
序列化属性的继承 对象的序列化属性没有办法被继承,换句话说,继承标注为 [Serialized] 类的子类,由其创建的实例对象,并没有办法被序列化,除非你明确地将其标注为[Serialized],如果基类同时亦实现接口ISerializable,派生的子类同样也必须实现方法GetObjectData() 以及构造函数,才能够自定义序列化成员变量的相关程序。另外在还原序列化重组对象时所调用的构造函数,同样必须继承基类。
序列化数据的修正 当一个成员变量被标注为 [NonSerialized] 时,由于在对象序列化的过程中,成员变量并没有被分解,当进行还原序列化操作,即重组对象的时候,所得到的将不是原先的成员变量的值。如果想要改变这种预设行为,可以在设计类的时候,继承IDeserializationCallback接口,实现方法OnDeserialization() ,还原正确的数据值。
方法OnDeserialization() 的定义如下: Void OnDeserialization(object sender);
继承IDeserializationCallback接口的类对象,在还原序列化的时候,会先调用OnDeserialization() 方法,执行其中的方法。
当我们操作一个包含大量的暂时性数据对象,而此时序列化对象的时候,会同时分解这些暂时性数据,连同分解后的对象一并写入文件,可以想象,这种作法非但没有意义,而且浪费资源。这时可以选择先把这些数据成员标注为 [NonSerialized] ,然后在还原序列化,重组对象的同时,调用方法OnDeserialization() ,还原先前未被序列化分解的成员变量,这样一来,除了序列化的过程比较有效率外,还能够兼顾数据的正确性。
序列化示例 序列化操作时需要引用的命名空间
using
System.Runtime.Serialization; //
包含可用于序列化和反序列化对象的类
using
System.Runtime.Serialization.Formatters.Binary; //
二进制序列化需要的命名空间
using
System.Runtime.Serialization.Formatters.Soap; //Soap
序列化需要的命名空间
using
System.Xml.Serialization; //XML
序列化需要的命名空间
|
BinaryFormatter方法
/*
定义一个简单的测试类,用来测试Binary和Soap方式序列化和反序列化 */
[Serializable] /*
定义类MText属性为可序列化类 */
public
class MText
{
public string str;
}
|
/* BinarySerialize
方法将执行二进制序列化操作 */
public
void BinarySerialize(MText mtx)
{
/*
创建文件流fs,并定义保存文件BinarySeri.txt */
FileStream
fs = new FileStream("BinarySeri.txt", FileMode.Create);
/*
实例化二进制序列化操作类BinaryFormatter */
BinaryFormatter
bf = new BinaryFormatter();
/*
调用Serialize方法进行二进制序列化操作,并将mtx的可序列化成员内容写进文件流fs 中进行存放 */
bf.Serialize(fs, mtx);
/*
关闭文件流 */
fs.Close();
}
/* BinaryDeSerialize
方法将执行二进制反序列化操作,并返回序列化类MText */
public
MText BinaryDeSerialize()
{
/*
定义并实例化MText类,用来保存反序列化得到的结果 */
MText mtx = new MText();
/*
创建文件流fs,并定义读取文件BinarySeri.txt */
FileStream
fs = new FileStream("BinarySeri.txt", FileMode.Open);
/*
实例化二进制序列化操作类BinaryFormatter */
BinaryFormatter bf = new BinaryFormatter();
/*
调用Deserialize方法进行二进制反序列化操作,并将转换后的结果赋给MText
变量mtx */
mtx = (MText)bf.Deserialize(fs);
/*
关闭文件流并反回结果 */
fs.Close();
return mtx;
}
|
SoapFormatter方法
/* SoapFormatter
方法与二进制序列化和反序列化大体相同,只是序列化时
所用的操作类由BinaryFormatter变为SoapFormatter,其它一致 */
Text
mtx = new MText();
SoapFormatter sf = new SoapFormatter();
sf.Serialize(fs, mtx); //
序列化操作
mtx = (MText)sf.Deserialize(fs); //
反序列化操作
|
XmlSerialize方法
/* XmlSerialize
方法与二进制序列化和反序列化大体相同,只是序列化时
所用的操作类由
BinaryFormatter
变为
XmlSerialize
,其它一致
*/
MText
mtx = new MText();
/*
在实例化XmlSerializer时,需要给出由typeof函数返回的可序列化对象的类型 */
XmlSerializer
xs = new XmlSerializer(typeof(MText));
xs.Serialize(fs, mtx); //
序列化操作
mtx = (MText)xs.Deserialize(fs); //
反序列化操作
|
方法详见二进制序列化。
ISerializable方法
/*
定义一个简单的测试类,用来测试ISerializable方式序列化和反序列化 */
[Serializable] /*
定义类RMText为可序列化类 */
public
class RMText : ISerializable /*
继承ISerializable接口创建专属Formatter */
{
public string str;
public RMText() { }
/*
特定构造函数,用来在对象反序列化时,将info参数内容传送到类 */
public RMText(SerializationInfo info, StreamingContext context)
{
str = (string)info.GetString("str");
}
/*
继承ISerializable接口所必须实现的方法,GetObjectData用来将对象相关信息填入
SerializationInfo
对象。在这个方法中,将指定的自定义序列化成员变量加入info,
强制Formatter序列化这些成员变量为当前的内容,成员变量以key/value的形式加入
Info
,也就是其中的每一个值均需有其对应的名称 */
public virtual void GetObjectData(SerializationInfo info,
StreamingContext
context)
{
info.AddValue("str", str);
}
}
|
自定义序列化类由任何可进行序列化操作的方法进行直接使用。
Inherit(继承)方法
[Serializable] //
定义类IMText为可序列化类
public
class IMText : RMText //
继承RMText类
{
public string istr;
public IMText() { }
/*
特定构造函数,用来在对象反序列化时,将info参数内容传送到类,并继承基类*/
public IMText(SerializationInfo info, StreamingContext context)
: base(info, context)
{
istr = (string)info.GetString("istr");
}
/*
继承ISerializable接口所必须实现的方法,GetObjectData用来将对象相关信息填 入SerializationInfo对象。在这个方法中,将指定的自定义序列化成员变量加入
Info
,强制Formatter序列化这些成员变量为当前的内容,成员变量以key/value的形式加入Info,也就是其中的每一个值均需有其对应的名称,并覆写基类的方法*/
public override void GetObjectData(SerializationInfo info,
StreamingContext context)
{
base.GetObjectData(info, context);
info.AddValue("istr", istr);
}
}
|
继承序列化类由任何可进行序列化操作的方法进行直接使用。
IDeserializationCallback方法
[Serializable] //
定义类NonMText为可序列化类
/*
继承接口IDeserializationCallback,在其中实现OnDeserialization方法还原正确的
数据 */
public
class NonMText : IDeserializationCallback
{
/*
标注成员变量为不可序列化 */
[NonSerialized]
public int num;
[NonSerialized]
public int tmp;
public NonMText()
{
num = 1000;
tmp = 2000;
}
/*
当Formatter还原序列化,重组对象时,会先调用OnDeserialization方法,重新
指定num变量的值,而tmp由于没有做修正,得到的结果将不同于原值2000 */
public void OnDeserialization(object sender)
{
num = 1000;
}
}
|
修正序列化类由任何可进行序列化操作的方法进行直接使用。
方法详见二进制序列化。