虽然我们平时都使用第三方库来进行序列化和反序列化,用起来也很方便,但至少得明白序列化与反序列化的基本原理。

懂得人就别看了!

注意:从.NET Framework 2.0 开始,序列化格式化器类SoapFormatter已过时。请改用 BinaryFormatter。

  • 序列化:把目标对象转换为字节流的过程
  • 反序列化:把字节流转换回对象的过程

序列化与反序列化需要一个序列化器类:即System.Runtime.Serialization.Formatters.Binary.BinaryFormatter

实例化一个格式化器BinaryFormatter类:BinaryFormatter binaryFormatter = new BinaryFormatter();

一、序列化:

binaryFormatter.Serialize(Stream stream, Object obj);

  • 该方法把一个或多个目标对象序列化为字节流并保存到目标流对象stream中。需要提供一个目标流对象stream(stream对象可以是基类Stream的派生类:FileStream、MemoryStream、NetWorkStream等流对象);

序列化过程:

  • 序列化时,首先判断每个对象的类型定义是否应用了可序列化[Serializable]特性,否则抛出异常。
  • 其次调用对象中那些被标记了[OnSerializing]特性的所有方法。(即:执行序列化前,先调用该方法做一些事情)
  • 接下来,利用反射机制来取得每个目标对象的类型中所有需要序列化的实例字段的信息,并读取对应字段的值保存到字节流中。
  • 序列化时,还保存了目标类型的全名、定义类型程序集的全名,作为标识信息保存到流中(用于反序列化)。
  • 最后,调用所有被标记了[OnSerialized]特性的方法。(即:序列化完成后,调用该方法做一些事情)

二、反序列化:

SomeObject obj = (SomeObject)binaryFormatter.Deserialize(Stream stream);

  • 该方法把目标流对象stream中的字节流反序列化为Object对象,可根据需求进行转型为对应的目标对象

反序列化过程:

  • 反序列化时,首先从字节流中读取程序集的标识信息,然后调用System.Reflection.Assembly类的Load()方法加载该程序集到当前的AppDomain中,只有当程序集加载成功后,格式化器才能在程序集中查找是否存在与需要被反序列化的对象的类型信息相同的类型
  • 找到类型后,调用对象中那些被标记了[OnDeserializing]特性的所有方法
  • 接下来利用该类型创建实例
  • 然后从字节流中获取对应字段的值对该实例进行初始化。
  • 最后,调用所有被标记了[OnDeserialized]特性的方法。

此过程中若找不到匹配的类型,则会抛出异常终止反序列化。但是,我在利用第三方类库反序列化JSON文件时,JSON文件并不存在对象名和程序集名的标识信息,第三方类库的格式化器应该是根据各个对象的成员名称、类型,在当前AppDomain中的所有程序集中进行查找匹配的类型。

注意:

  • 最好把序列化或反序列化的过程放进try块中,用catch (SerializationException e)块处理需要处理的异常,并在finally块中释放资源(关闭流)。
  • 可以把流的定义放在using语句的“()”中,把序列化反序列化代码放在using块内,来达到自动释放资源的目的。

三、序列化配置

在执行序列化和反序列化之前,可以对格式化器的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中反序列化后得到的对象的成员是一个基类类型的,如果直接把该成员强转为我们实际需要的子类类型,正常情况下是不行的,这时就需要自定义一些方法来实现了,利用协变在序列化过程中返回子类类型的对象给基类类型,这样得到的基类类型就可以强转为子类类型了。具体实现代码,大家可以搜起来!

细说C#中的序列化与反序列化的基本原理和过程的更多相关文章

  1. Net中JSON序列化和反序列化处理(日期时间特殊处理)

    0  缘由 笔者最近在web api端使用Json.Net进行序列化处理,而在调用端使用DataContractSerializer进行反序列化,遇到日期时间处理反序列化不成功[备注:笔者使用Net ...

  2. day5-python中的序列化与反序列化-json&pickle

    一.概述 玩过稍微大型一点的游戏的朋友都知道,很多游戏的存档功能使得我们可以方便地迅速进入上一次退出的状态(包括装备.等级.经验值等在内的一切运行时数据),那么在程序开发中也存在这样的需求:比较简单的 ...

  3. 在net中json序列化与反序列化 面向对象六大原则 (第一篇) 一步一步带你了解linq to Object 10分钟浅谈泛型协变与逆变

    在net中json序列化与反序列化   准备好饮料,我们一起来玩玩JSON,什么是Json:一种数据表示形式,JSON:JavaScript Object Notation对象表示法 Json语法规则 ...

  4. 浅谈C#中的序列化与反序列化

    今天我利用这篇文章给大家讲解一下C#中的序列化与反序列化.这两个概念我们再开发中经常用到,但是我们绝大部分只用到了其中的一部分,剩下的部分很多开发人员并不清楚,甚至可以说是不知道.因此我希望通过这篇文 ...

  5. javaIO中的序列化和反序列化

    javaIO中的序列化和反序列化 1.什么是序列化?它是来解决什么问题的 1.我们创建的对象,一般情况下在内存中,程序关闭,或者因为没有地址指向而导致垃圾回收 2.这样,我们的对象就会丢失 3.那么我 ...

  6. C#中JSON序列化和反序列化

    有一段时间没有到博客园写技术博客了,不过每天逛逛博客园中大牛的博客还是有的,学无止境…… 最近在写些调用他人接口的程序,用到了大量的JSON.XML序列化和反序列化,今天就来总结下json的序列化和反 ...

  7. C#中的 序列化和反序列化

    什么是序列化和反序列化? 序列化就是把一个对象保存到一个文件或数据库字段中去,反序列化就是在适当的时候把这个文件再转化成原来的对象使用. 我想最主要的作用有: 1.在进程下次启动时读取上次保存的对象的 ...

  8. C#中的序列化与反序列化

    眼看XX鸟的课程关于C#的知识点就要学完了,翻看网络中流传的教程还是发现了一个课程中没有讲到的知识点:序列化与反序列化 无奈还是了解一下并操作一番,以备后用吧 介绍:就是将对象信息转化为二进制信息以便 ...

  9. K:java中的序列化与反序列化

    Java序列化与反序列化是什么?为什么需要序列化与反序列化?如何实现Java序列化与反序列化?以下内容将围绕这些问题进行展开讨论. Java序列化与反序列化 简单来说Java序列化是指把Java对象转 ...

随机推荐

  1. 初学python - 零碎的知识点

    * 标识符命名规则 数字,字母,下划线组成,数字不能开头区分大小写 * 注释 单行:#:多行:‘ ‘ ‘ ,“”“: * 进制标识 b:二进制 o:八进制 d:十进制x:十六进制 转换:format( ...

  2. 多线程《七》信号量,Event,定时器

    一 信号量 信号量也是一把锁,可以指定信号量为5,对比互斥锁同一时间只能有一个任务抢到锁去执行,信号量同一时间可以有5个任务拿到锁去执行,如果说互斥锁是合租房屋的人去抢一个厕所,那么信号量就相当于一群 ...

  3. Atcoder Grand Contest 032C(欧拉回路,DFS判环)

    #include<bits/stdc++.h>using namespace std;int vis[100007];vector<int>v[100007];vector&l ...

  4. js 封装常用方法

    1. 获取数据类型 function getType(params) { , -) } 2. 深拷贝 function deepCopy(params) { var obj; if (typeof p ...

  5. "window.location.href"、"location.href"是本页面跳转

    "window.location.href"."location.href"是本页面跳转 "parent.location.href"是上一 ...

  6. [CQOI2006]凸多边形(半平面交)

    很明显是一道半平面交的题. 先说一下半平面交的步骤: 1.用点向法(点+向量)表示直线 2.极角排序,若极角相同,按相对位置排序. 3.去重,极角相同的保留更优的 4.枚举边维护双端队列 5.求答案 ...

  7. javascript jquery console调试方法说明

    控制台(Console)是Firebug的第一个面板,也是最重要的面板,主要作用是显示网页加载过程中产生各类信息. 一.显示信息的命令 Firebug内置一个console对象,提供5种方法,用来显示 ...

  8. zip与unzip

  9. 洛谷 P1155 【NOIP2008】双栈排序

    题目链接 题解 这题有点神啊.. 我们仔细观察一下,发现两个栈内元素必须为降序 那么有结论 如果有\(i < j < k\) 且 \(a[k] < a[i] < a[j]\)则 ...

  10. Odd number problem

    描述 你一定玩过八数码游戏,它实际上是在一个3*3的网格中进行的,1个空格和1~8这8个数字恰好不重不漏地分布在这3*3的网格中.例如:5 2 81 3 _4 6 7在游戏过程中,可以把空格与其上.下 ...