虽然我们平时都使用第三方库来进行序列化和反序列化,用起来也很方便,但至少得明白序列化与反序列化的基本原理。
懂得人就别看了!
注意:从.NET Framework 2.0 开始,序列化格式化器类SoapFormatter已过时。请改用 BinaryFormatter。
序列化与反序列化需要一个序列化器类:即System.Runtime.Serialization.Formatters.Binary.BinaryFormatter类
实例化一个格式化器BinaryFormatter类:BinaryFormatter binaryFormatter = new BinaryFormatter();
binaryFormatter.Serialize(Stream stream, Object obj);
FileStream、MemoryStream、NetWorkStream等流对象);
序列化过程:
[Serializable]特性,否则抛出异常。[OnSerializing]特性的所有方法。(即:执行序列化前,先调用该方法做一些事情)[OnSerialized]特性的方法。(即:序列化完成后,调用该方法做一些事情)
SomeObject obj = (SomeObject)binaryFormatter.Deserialize(Stream stream);
反序列化过程:
System.Reflection.Assembly类的Load()方法加载该程序集到当前的AppDomain中,只有当程序集加载成功后,格式化器才能在程序集中查找是否存在与需要被反序列化的对象的类型信息相同的类型[OnDeserializing]特性的所有方法[OnDeserialized]特性的方法。
此过程中若找不到匹配的类型,则会抛出异常终止反序列化。但是,我在利用第三方类库反序列化JSON文件时,JSON文件并不存在对象名和程序集名的标识信息,第三方类库的格式化器应该是根据各个对象的成员名称、类型,在当前AppDomain中的所有程序集中进行查找匹配的类型。
注意:
在执行序列化和反序列化之前,可以对格式化器的Context属性进行设置,Context属性是一个StreamingContext结构
binaryFormatter.Context = new StreamingContext(StreamingContextStates.Remoting);//指定为来源和目的地是远程的
StreamingContext结构的属性有两个:
State属性,是一个枚举类型StreamingContextStates的值,用来说明序列化和反序列化的对象的来源和目的地Context属性,一个上下文对象的引用,包含了用户希望得到的任何上下文信息
StreamingContext结构存在的意义就是通过State属性的值描述给定的序列化流的源和目标,并利用Context属性提供一个由调用方定义的附加上下文。
(因为同一个被序列化好的对象可能会有不同的目的地,如不同机器,不同进程中等,我们就可以通过State属性的状态来标识对象的目的地)
示例:利用序列化和反序列化,定义一个深度克隆一个对象的方法
public object DeepCloneObject(object oldObj)
{
try
{
using (MemoryStream stream = new MemoryStream())
{
BinaryFormatter bf = new BinaryFormatter();
bf.Context = new StreamingContext(StreamingContextStates.Clone);
//把对象序列化到流中
bf.Serialize(stream, oldObj);
//在进行反序列化前,需要先定位到内存流的起始位置
stream.Position = 0;
//将内存流中的内容反序列化成新的对象
return bf.Deserialize(stream);
}
}
catch(SerializationException e)
{
Console.WriteLine("序列化和反序列化时出错了,错误信息为:" + e.Message);
//不做处理,重新抛出原异常对象
throw;
}
finally
{
//using中的stream对象会被自动释放,这里不要对它处理
}
}
ISerializationCallbackReceiver,该接口定义了两个方法:比如在Unity3d中,无法序列化枚举类型的成员,就需要把他标记为[NonSerialized]不对他处理,然后定义一个该成员的字符串形式的成员,通过该字符串和对应枚举类型的转换,就可以达到序列化的目的了。如下代码:
public enum ItemType
{
left,
right
}
public class ScoreModel:ISerializationCallbackReceiver
{
public int Score { get; set; }
[NonSerialized]
public ItemType itemType;
public string itemTypeString;
//反序列化完成自动后调用
public void OnAfterDeserialize()
{
itemType = (ItemType)Enum.Parse(typeof(ItemType), itemTypeString);
}
//进行序列化之前自动调用
public void OnBeforeSerialize()
{
itemTypeString = itemType.ToString();
}
}
即使是第三方类库,可能也无法处理这种情况,比如:从JSON中反序列化后得到的对象的成员是一个基类类型的,如果直接把该成员强转为我们实际需要的子类类型,正常情况下是不行的,这时就需要自定义一些方法来实现了,利用协变在序列化过程中返回子类类型的对象给基类类型,这样得到的基类类型就可以强转为子类类型了。