通俗的来说,序列化主要用于将存储对象状转换为另一种通用格式,比如存储为二进制、xml、json等等,而反序列化通常是从这种格式转换回来的过程。
使用序列化主要是因为跨平台和对象存储的需求,因为网络上只允许字符串或者二进制格式,而文件需要使用二进制流格式,如果想把一个内存中的对象存储下来就必须使用序列化转换为xml(字符串)、json(字符串)或二进制(流)等。
一、序列化和反序列化产生原理
互联网的产生带来了机器间通讯的需求,而互联通讯的双方需要采用约定的协议,序列化和反序列化属于通讯协议的一部分。通讯协议往往采用分层模型,不同模型每层的功能定义以及颗粒度不同,例如:TCP/IP协议是一个四层协议,而OSI模型却是七层协议模型。在OSI七层协议模型中展现层(Presentation Layer)的主要功能是把应用层的对象转换成一段连续的二进制串,或者反过来,把二进制串转换成应用层的对象–这两个功能就是序列化和反序列化。一般而言,TCP/IP协议的应用层对应与OSI七层协议模型的应用层,展示层和会话层,所以序列化协议属于TCP/IP协议应用层的一部分。本文对序列化协议的讲解主要基于OSI七层协议模型。
- 序列化: 将数据结构或对象转换成二进制串的过程
- 反序列化:将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程
二、序列化的作用
1 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;
2 在网络上传送对象的字节序列。
在很多应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便长期保存。比如最常见的是Web服务器中的Session对象,当有 10万用户并发访问,就有可能出现10万个Session对象,内存可能吃不消,于是Web容器就会把一些seesion先序列化到硬盘中,等要用了,再把保存在硬盘中的对象还原到内存中。
当两个进程在进行远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据,都会以二进制序列的形式在网络上传送。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象。
三、几种常见的序列化和反序列化协议
XML
XML是一种常用的序列化和反序列化协议,具有跨机器,跨语言等优点。 XML历史悠久,其1.0版本早在1998年就形成标准,并被广泛使用至今。XML的最初产生目标是对互联网文档(Document)进行标记,所以它的设计理念中就包含了对于人和机器都具备可读性。 但是,当这种标记文档的设计被用来序列化对象的时候,就显得冗长而复杂(Verbose and Complex)。 XML本质上是一种描述语言,并且具有自我描述(Self-describing)的属性,所以XML自身就被用于XML序列化的IDL。 标准的XML描述格式有两种:DTD(Document Type Definition)和XSD(XML Schema Definition)。作为一种人眼可读(Human-readable)的描述语言,XML被广泛使用在配置文件中,例如O/R mapping、 Spring Bean Configuration File 等。
典型应用场景包括:
XML所具有的人眼可读(Human-readable)特性使得其具有出众的可调试性,互联网带宽的日益剧增也大大弥补了其空间开销大(Verbose)的缺点。对于在公司之间传输数据量相对小或者实时性要求相对低(例如秒级别)的服务是一个好的选择。
由于XML的额外空间开销大,序列化之后的数据量剧增,对于数据量巨大序列持久化应用常景,这意味着巨大的内存和磁盘开销,不太适合XML。另外,XML的序列化和反序列化的空间和时间开销都比较大,对于对性能要求在ms级别的服务,不推荐使用。WSDL虽然具备了描述对象的能力,SOAP的S代表的也是simple,但是SOAP的使用绝对不简单。对于习惯于面向对象编程的用户,WSDL文件不直观。
JSON(Javascript Object Notation)
JSON起源于弱类型语言Javascript, 它的产生来自于一种称之为”Associative array”的概念,其本质是就是采用”Attribute-value”的方式来描述对象。实际上在Javascript和PHP等弱类型语言中,类的描述方式就是Associative array。JSON的如下优点,使得它快速成为最广泛使用的序列化协议之一:
1、这种Associative array格式非常符合工程师对对象的理解。
2、它保持了XML的人眼可读(Human-readable)的优点。
3、相对于XML而言,序列化后的数据更加简洁。 来自于的以下链接的研究表明:XML所产生序列化之后文件的大小接近JSON的两倍
4、它具备Javascript的先天性支持,所以被广泛应用于Web browser的应用常景中,是Ajax的事实标准协议。
5、与XML相比,其协议比较简单,解析速度比较快。
6、松散的Associative array使得其具有良好的可扩展性和兼容性。
典型应用场景和非应用场景
JSON在很多应用场景中可以替代XML,更简洁并且解析速度更快。
典型应用场景包括:
1、公司之间传输数据量相对小,实时性要求相对低(例如秒级别)的服务。
2、基于Web browser的Ajax请求。
3、由于JSON具有非常强的前后兼容性,对于接口经常发生变化,并对可调式性要求高的场景,例如Mobile app与服务端的通讯。
总的来说,采用JSON进行序列化的额外空间开销比较大,对于大数据量服务或持久化,这意味着巨大的内存和磁盘开销,这种场景不适合。没有统一可用的IDL降低了对参与方的约束,实际操作中往往只能采用文档方式来进行约定,这可能会给调试带来一些不便,延长开发周期。 由于JSON在一些语言中的序列化和反序列化需要采用反射机制,所以在性能要求为ms级别,不建议使用。
四、C#三种序列化和反序列化
4.1 基础类型数据的序列化和反序列化
常见的基础类型数据有int、float、char、double、bool等,对这些数据进行序列化操作是,需要用到BitConverter类的相关方法进行处理。
//对基础类型数据进行编码和解码
int a = 123;
float f = 1.25f;
byte[] b1 = BitConverter.GetBytes(a);//整形数据序列化
byte[] b2 = BitConverter.GetBytes(f);//float类型数据序列化
byte[] temp = new byte[b1.Length + b2.Length];
Array.Copy(b1, 0, temp, 0, b1.Length);
b2.CopyTo(temp, b1.Length);
//对接收的数据解码
int i1 = BitConverter.ToInt32(temp, 0);
float f2 = BitConverter.ToSingle(temp, 4);
//int i1= BitConverter.ToInt32(new byte[]{123,1,0,0,0},1);//从给定的起始位置,读取对应数据长度的字节,然后将其转化为对应的数据
Console.WriteLine(i1 + "----" + f2);
4.2 字符串数据的序列化和反序列化
字符串数据进行序列化和反序列化操作的时候要先确定编码方式,然后通过该编码方式,对字符串进行序列化。在反序列化操作的时候,要以相同的编码方式进行解码。含有中文的字符串需要使用UTF8进行编码和解码。
//字符串数据的序列化
string st = "你好";
byte[] bb = Encoding.UTF8.GetBytes(st);
for (int i = 0; i < bb.Length; i++)
{
Console.WriteLine(bb[i]);//byte 字节,1byte=8bite,
}
//字符串数据的反序列化
string res= Encoding.UTF8.GetString(new byte[] { 228,189,160});
Console.WriteLine(res);
4.3对自定义类的序列化和反序列化
自定义类序列化操作的时候,需要先转化为数据流,然后将数据流进行序列化和反序列化操作。
注意:需要被序列化的类可以写在一个类库里,把类库发布出dll文件,在客户端和服务器进行引用,否则可能会产生异常。同时需要被序列化的类添加序列化标签 [Serializable]
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading.Tasks;
namespace MyTools
{
/// <summary>
/// 序列化和反序列化管理工具类
/// </summary>
public class SerializeManage
{
/// <summary>
/// 序列化操作
/// </summary>
/// <param name="obj">自定义类</param>
/// <returns>字节数组</returns>
public byte[] SerializeObj(Object obj)
{
MemoryStream ms = new MemoryStream();
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(ms, obj);
byte[] data = ms.ToArray();
return data;
}
/// <summary>
/// 反序列化操作
/// </summary>
/// <typeparam name="T">自定义类的类型</typeparam>
/// <param name="data">需要反序列化的字节数组</param>
/// <returns>自定义类反序列化对象</returns>
public T deSerializeObj<T>(byte[] data)
{
MemoryStream ms1 = new MemoryStream(data);
BinaryFormatter bf1 = new BinaryFormatter();
T type = (T)bf1.Deserialize(ms1);
return type;
}
}
}