0%

序列化与反序列化简介

先来个背景音乐。

简介

想必读者对编程中的「变量」、「实例(对象)」不会陌生。

变量和实例对应的,是一些有意义的数据。这些数据,在不同编程语言中,可能有不一样的表现;在不同操作系统中,也可能有不一样的形式。现代计算机应用,不可避免地会涉及到多个模块/程序之间的数据交换、不同语言之间的相互协作、计算机之间的数据交互。如果依着操作系统和编程语言的特性,用不同的形式去实现同样的数据,那整个计算机世界就会乱了套了。

数据的序列化和反序列化,就是为了解决这个问题而诞生的。

序列化是说,将变量和实例这些数据,依照某种约定,转化(通常伴随着压缩)为一种通用的数据格式;转化后的数据,可以用来储存或者传输,以备下次读取使用。其中提到的格式可以是二进制的,也可以是字符串式的。反序列化,就是上述过程的补集:将序列化的数据读入,解析为编程语言可识别的数据结构的过程。

序列化协议的特点

从上面的简介可以看出,数据序列化的目的是解决跨变成语言、跨操作系统平台的数据传输和储存的问题。这样一来,一个好的序列化协议,就必须:

  1. 跨平台,并有多语言支持:这是根本目的。
  2. 足够流行:流行程度高意味着使用者众多,协议的基础设施迭代更新快。
  3. 足够成熟:协议应该经过足够的测试,和足够长时间的检验。
  4. 语言/平台的中立性:协议应该对各个编程语言、操作系统平台一碗水端平,对各个语言、平台的特性支持保持一致。
  5. 可读性:如果数据序列化之后的数据人眼可读,那么开发过程中对序列化数据的调试就会变得非常简单。
  6. 性能:为了保持原有的数据结构,数据在序列化的过程中,必然会加进额外的描述字段。加入额外的字段,必然会使序列化后的大小大于纯数据的大小,同时也会涉及到序列化、反序列化对描述字段的解读效率的问题。因此一个好的序列化协议,应该在空间复杂度和时间复杂度上有好的表现。

序列化协议的结构

那么,一个设计良好的序列化协议,应该由那几部分组成呢?

IDL

IDL 是 Interface description language(接口描述语言)的首字母缩写。

上文提到,一个设计良好的序列化协议,应该能够跨平台并支持多种程序设计语言。这也就是说,如果 Alice 在 $A$ 系统上使用 $\alpha$ 语言进行编程,同时 Bob 在 $B$ 系统上使用 $\beta$ 语言进行编程,如果 Alice 和 Bob 使用相同的序列化协议,那么两人的程序序列化的结果在格式上要完全一致,以保证二者的程序可以相互通信。由于操作系统或编程语言的不同,让 Alice 和 Bob 用不同的语言写出完全一样的序列化结构是不现实的,因此我们需要在 Alice 和 Bob 之间搭一座桥梁:这座桥梁可以用于操作系统平台及编程语言无关的方式,描述清楚用于交换的数据格式。这样一来,Alice 和 Bob 只要共同维护用这种方式叙述的清单即可。

这座桥梁,就是所谓的 IDL;用 IDL 叙述的数据原型清单,就是所谓的 IDL 文件。

IDL 编译器

接着上文的例子。

假设 Alice 和 Bob 已经共同用 IDL 维护了一份 IDL 文件,现在 Alice 和 Bob 如何分别在 $\alpha$, $\beta$ 语言中使用在 IDL 文件中定义的数据原型呢?以 C++ 为例,我们拿到一份 IDL 文件,就会希望根据这份 IDL 文件,构建一个类。类中包含 IDL 中定义的全部数据原型——他们都应该是 private 的,同时提供 get()set() 接口等等。对于其他语言,也应该有类似的功能。

如果有一个用表意精确的 IDL 语法书写的 IDL 文件,那么根据这个文件手工翻译出各个编程语言的类,显然是不可取的。为了自动化地完成这一步工作,一个设计良好的序列化协议,应该针对各个编程语言设计相应的 IDL 编译器:将 IDL 文件编译(翻译)为相应编程语言的类或动态库。

序列化和反序列化

这自然是序列化协议中不可缺少的一部分——毕竟序列化协议就是为此而生的嘛。(笑)

作为一套软件的客户端和服务端,显而易见,二者都应该同时具备对序列化和反序列化的能力:

  • 客户端接受应用层的参数和数据,将其序列化后通过底层传输给服务端;同时,反序列化服务端发会的数据。
  • 服务端接受客户端发来的序列化数据,反序列化后进行相应的处理;再将结果序列化后发回给客户端。

尽管二者职能相近,但是大家给工作在客户端的序列化/反序列化组件起名为 Stub,相对的工作在服务端的组件则被称为 Skeleton。

与数据库做对比

数据库作为数据以一定方式储存在一起、能为多个用户共享、具有尽可能小的冗余度、与应用程序彼此独立的数据集合,其形式与功能和序列化协议有相当高的相似程度。只不过,数据库主要提供了数据存储的场所,而序列化协议则主要提供数据在模块之间通讯的数据标准。

下表将序列化协议和数据库中的组件做了一个简单的对应:

序列化协议 数据库组件
接口描述语言(IDL) 数据定义语言(DDL)
接口描述语言文件(IDL File) 数据库架构(DB Schema)
Stub/Skeleton lib Object Relational Mapping
俗话说,投资效率是最好的投资。 如果您感觉我的文章质量不错,读后收获很大,预计能为您提高 10% 的工作效率,不妨小额捐助我一下,让我有动力继续写出更多好文章。