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

懂得人就别看了!

注意:从.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. 标准模板库使用参考——vector向量容器

    C++的STL从广义上讲分为algorithm(算法),container(容器)和iterator(迭代器)三类,包含了诸多在计算机科学领域里所常用的基本数据结构和基本算法. 在C++标准库中,ST ...

  2. Java程序员修炼之路

    作者简介:王成委,CSDN知识库特邀编辑,Java高级工程师,熟悉Java编程语言和Oracle数据库.专注于高并发架构设计和大数据存储方向的研究. 我们为什么选择Java 大多数人选择Java可能只 ...

  3. 《 V I M 教 程 》 —— 版本 1.7

    =============================================================================== = 欢 迎 阅 读 < V I M ...

  4. node创建一个简单的web服务

    本文将如何用node创建一个简单的web服务,过程也很简单呢~ 开始之前要先安装node.js 1.创建一个最简单的服务 // server.js const http = require('http ...

  5. 谈谈easyui datagrid 的数据加载

    文章目录 1url方式加载数据 1.1调用方式 1.2相关方法 1.3二次加载问题 2加载本地数据方式 2.1调用方式 2.2如何分页 2.3加载中效果 2.4如何不统计总数 这篇文章只谈jQuery ...

  6. [o.e.b.ElasticsearchUncaughtExceptionHandler] [unknown] uncaught exception in thread [main] org.elasticsearch.bootstrap.StartupException: java.lang.RuntimeException: can not run elasticsearch as root

    这是因为不知从哪个版本后,elasticsearch为了安全性,是不能用root用户启动的. 解决的办法:当然是创建一个用户,用创建的用户启动啦,注意权限的问题,目录也应该改为创建的用户权限! 我是用 ...

  7. [DEBUG]-[org.mybatis.spring.SqlSessionFactoryBean.buildSqlSessionFactory(SqlSessionFactoryBean.java:431)] 一直在创建sqlsession工厂原因

    今天在做开发项目的时候出现一直在控台输出创建sqlsessionfactory'这个代码, tomcat一直在控制台上输出这个内容无法停止下来,那么到底是什么原因呢, 我们可以在输出信息上看到有个wa ...

  8. sql随机插入数据--记录

    sql面试题中经常出现一张学生表,表字段有学生ID,学生课程,学生成绩 今天要实测,so,需要有数据,now,随机生成数据,,, create table student ( id ), --编号 c ...

  9. 制作一句话图片马(NO)

  10. ui-grid 中cellTemplate中click事件

    cellTemplate中使用的函数: 外部定义的函数: