序列化

序列化（serialization）在计算机科学的资料处理中，是指将资料结构或物件状态转换成可取用格式（例如存成档案，存于缓冲，或经由网络中传送），以留待后续在相同或另一台计算机环境中，能恢复-{}-原先状态的过程。依照序列化格式重新获取位元组的结果时，可以利用它来产生与原始物件相同语义的副本。对于许多物件，像是使用大量参照的复杂物件，这种序列化重建的过程并不容易。物件导向中的物件序列化，并不概括之前原始物件所关联的函式。这种过程也称为物件编组（marshalling）。从一系列位元组提取资料结构的反向操作，是反序列化（也称为解编组、deserialization、unmarshalling）。
序列化在计算机科学中通常有以下定义：
*对同步控制而言，表示强制在同一时间内进行单一存取。
*在数据储存与传送的部分是指将一个对象存储至一个储存媒介，例如档案或是记忆体缓冲等，或者透过网络传送资料时进行编码的过程，可以是位元组或是XML等格式。而位元组的或XML编码格式可以还原完全相等的对象。这程序被应用在不同应用程式之间传送对象，以及伺服器将对象储存到档案或资料库。相反的过程又称为反序列化。

**用途**
经由电信线路传输资料的方法（通讯）。
*储存资料的方法（在资料库或硬碟）。
*远端程序调用的方法，例如在SOAP中。
*在以元件为基础，例如COM，CORBA的软体工程中，是物件的分散式方法。
*检测随时间资料变动的方法。
为了达成上述功能其一能有效作用，则必须与硬体结构保持独立性。譬如说为了能最大化分散式的使用，在不同硬体运行的计算机，应该能够可靠地重建序列化资料流，而不依赖于位元组序。虽然直接复拷记忆体中的资料结构更简便又快速，可是对于其它不同硬体的机器，却无法可靠地运作。以独立于硬体之外的格式来序列化资料结构，要避开位元组序、记忆体布局、或在不同编程语言中资料结构如何表示等等之类的问题。
对于任何序列化方案的本质来说，因为资料编码是根据定义连续串在一起的，提取序列化资料结构中的某一部份，则需要从头到尾读取整个物件并且重新建构。这样的资料线性在许多应用中是有利的，因为它使输出入介面简单而共同，能被用来保持及传递物件的状态。
要求高效能的应用时，花费精力处理更复杂的非线性储存系统是有其必要意义的。即使在单一机器上，原始的指针物件也非常脆弱无法保存，因为它们指向的标地可能重新加载到内存中的不同位址。为了处理这个问题，序列化过程包括一个步骤：将参照的直接指针转换为以名称或位置的间接参照，称之为不挥发（unswizzling）或者指针不挥发。反序列化过程则包括了称为指针旋转（swizzling）的反向步骤。由于序列化和反序列化可从共通代码（例如，微软MFC中的Serialize函数）驱动，所以共通代码可同时进行两次，因此，
#检测要序列化的物件与其先前副本之间的差异，
#提供下一次这种检测的输入。因为差异可以被即时检测，所以不必再重新建立先前的副本。该技术称为差异分辨执行。

这技术应用在内容随时间变化的使用者界面编程中－依照输入事件来处理图形物件的产生、移除、更改或制作，而无需编写另外的代码执行这些操作。

**缺点**
序列化可能会破解抽象资料型别的封装实作，而使其详细内容曝光。简单的序列化实作可能违反物件导向中私有资料成员需要封装（encapsulation）的原则。商用软体的出版商通常会将应用软体的序列化格式，当作商业秘密，以阻碍竞争对手生产可相容的产品；有些会蓄意地混淆，或甚至将序列化资料作加密处理。然而，互通可用性的要求应用程式能够理解彼此的序列化格式。因此，像CORBA的远端方法调用架构详细定义了它们的序列化格式。许多机构，例如档案馆和图书馆，尝试将他们的备份档案－特别是资料库抛档（dump），储存成一些相对具可读性的序列化格式中，使备份资料不因资讯技术变迁而过时。

**序列化格式**
20世纪80年代初的全录网络系统快递技术影响了第一个广泛采用的标准。Sun Microsystems在1987年发布了外部数据表示法（XDR）。90年代后期开始推动标准序列化的协议：XML（可延伸标记式语言）应用于产生人类可读的文字编码。资料以这样的编码使存续的物件能有效用，无论相对于人是否可阅读与理解，或与编程语言无关地传递给其它资讯系统。它缺点是失去了扎实的编码位元组流，但截至目前技术上所提供大量的储存和传输容量，使得档案大小的考量，已不同于早期计算机科学的重视程度。
二进制XML被提议作为一种妥协方式，它不能被纯文字编辑器读取，但比一般XML更为扎实。在二十一世纪的Ajax技术网页中，XML经常应用于结构化资料在客端和伺服端之间的异步传输。相较于XML，JSON是一种轻量级的纯文字替代，也常用于网页应用中的客端－伺服端通讯。JSON肇基于JavaScript语法所衍生，但也广为其它编程语言所支援。与JSON类似的另一个替代方案是YAML，它包含加强序列化的功能，更“人性化”而且更扎实。这些功能包括标记资料型别，支援非阶层式资料结构，缩排结构化资料的选项以及多种形式的纯量资料引用的概念。
另一种可读的序列化格式是属性列表（property list）。应用在NeXTSTEP、GNUstep和macOS Cocoa环境中。
针对于科学使用的大量资料集合，例如气候，海洋模型和卫星数据，已经开发了特定的二进制序列化标准，例如HDF，netCDF和较旧的GRIB。

**编程语言支援**
一些物件导向的编程语言直接支援物件序列化（或物件归档），可借由语法糖元素或者提供了标准介面。这些编程语言其中有Ruby，Smalltalk，Python，PHP，Objective-C，Delphi，Java 和.NET系列语言。若是缺少原生支援序列化的编程语言，也可使用额外的函式库来添加功能。

**C/C++**
C 和 C++ 没有提供任何类型的高阶序列化构造，但是两种语言都支援将内建资料型别以及一般的资料结构(struct)输出为二进制资料。因此，开发人员自己定义序列化函数是显而易举的。此外，基于编译器的解决方案，如用于 C++ 的ODB ORM系统，能够自动产生类别宣告的序列化源码，不必修改或仅少量的修改。其它普及的序列化框架是有来自Boost框架的Boost.Serialization，S11n和Cereal等框架。微软的MFC框架也提供序列化方法，作为其文件视图(Document-View)架构的部件。

**Java**
Java 提供自动序列化，需要以java.io.Serializable接口的实例来标明对象。实作接口将类别标明为“可序列化”，然后Java在内部处理序列化。在Serializable介面上并没有预先定义序列化的方法，但可序列化类别可任意定义某些特定名称和签署的方法，如果这些方法有定义了，可被调用执行序列化/反序列化部份过程。该语言允许开发人员以另一个Externalizable介面，更彻底地实作并覆盖序列化过程，这个介面包括了保存和恢复物件状态的两种特殊方法。
在预设情况下有三个主要原因使物件无法被序列化。其一，在序列化状态下并不是所有的物件都能获取到有用的语义。例如，Thread物件绑定到当前Java虚拟机的状态，对Thread物件状态的反序列化环境来说，没有意义。其二，物件的序列化状态构成其类别相容性缔结（compatibility contract）的某一部份。在维护可序列化类别之间的相容性时，需要额外的精力和考量。所以，使类别可序列化需要慎重的设计决策而非预设情况。其三，序列化允许存取类别的永久私有成员，包含敏感资讯（例如，密码）的类别不应该是可序列化的，也不能外部化。上述三种情形，必须实作Serializable介面来存取Java内部的序列化机制。标准的编码方法将栏位简单转换为位元组流。
原生型别以及永久和非静态的物件参照，会被编码到位元组流之中。序列化物件参照的每个物件，若其中未标明为transient的栏位，也必须被序列化；如果整个过程中，参照到的任何永久物件不能序列化，则这个过程会失败。开发人员可将物件标记为暂时的，或针对物件重新定义的序列化，来影响序列化的处理过程，以截断参照图的某些部份而不序列化。Java并不使用构造函数来序列化对象。
由JDBC也可对Java物件进行序列化，并将其储存到资料库中。虽然Swing元件的确实例化了Serializable接口，但它们不能移植到有版本差异的Java虚拟机之间。因此，Swing元件或任何继承它的元件可以序列化为位元组阵列，但不能保证这个仓存在另一台机器上可读取。

**Perl**
由CPAN所提供的几个Perl模组提供序列化机制，包括了Storable，JSON::XS和FreezeThaw。
Storable包括将档案或Perl纯量的资料结构，将其序列化和反序列化的功能。除了直接序列化到档案之外，Storable
还包含了冻结功能，将包装为纯量的资料，返回其序列化的副本；并可用thaw(解冻)这个纯量来反序列化。这对于以网路插座（socket）发送复杂的资料结构，或将其储存于资料库中非常有用。
当利用Storable对结构进行序列化时，具备了网路安全性的功能，它们以降低一点效能的成本，将资料储存为任何计算机可读取的格式。这些功能的名称有nstore，nfreeze等。依硬体特定的，带字母“n”函数所序列化的这些结构，则以没有字母“n”的函数将之反序列化－常态地解冻并撷取反序列化结构。

**PHP**
PHP最初通过内建的serialize()和unserialize()函数来实作序列化。PHP可以序列化任何其它资料类型，除了资源（档案指针，socket等）以外。对不受信任的资料上使用内建的unserialize()函数时，通常是有风险的。对于物件有两种“魔术方法”，__sleep()和__wakeup()，可以在类别中实作。而会分别从serialize()和unserialize()中呼叫，对应于清理和恢复物件的功能。例如，在序列化时可能需要关闭资料库连线，并在反序列化时恢复连线；这个功能可在这两种魔术方法中处理。它们也允许物件选择哪些属性可被序列化。从PHP 5.1开始有物件导向的序列化机制，即为Serializable介面。

**Python**
Python编程核心的序列化机制是pickle标准函式库，这名称暗示资料库相关的特别术语“浸渍”，来描述资料反序列化（unpickling for deserializing）。Pickle 使用一个简单的基于堆叠的虚拟机来记录用于重建物件的指令。这是个跨版本并可自订定义的序列化格式，但并不安全（不能防止错误或恶意资料）。错误格式或蓄意构建的资料，可能导致序列反解器汇入任意模组，而且实例化任何物件。
这个函式库有另外包括序列化为标准资料格式的模组：json（内置的基本纯量与集合型别支援，且能够通过编解码支援任何型别）和XML编码的属性列表（plistlib），限于plist支援的类型（数字，字串，布林，元组，串列，字典，日期时间和二进制blob）。最后，建议在正确的环境中评估物件的__repr__，使其和Common Lisp的打印物件大略地相符合。并非所有物件类型可以自动浸渍，特别是那些拥有操作系统资源（如档案把柄）的，但开发人员能注册自订定义的“缩减”和构造功能，来支援任何型别的浸渍和序列化。
Pickle最初是纯粹以Python编程语言来实作的模组，但在Python 3之前的版本中，cPickle模组（也是内建的）提供了更快速的性能。cPickle从Unladen Swallow专案改造而成。在Python 3中，开发人员应该导入标准版本，该版本会尝试导入加速版本并返回纯Python版本。

**.NET Framework**
.NET框架有几个由微软设计的序列化器。第三方协力厂商也有许多序列化器。

**Delphi**
Delphi提供将元件（也称为持续物件）序列化的内建机制，完全与开发环境整合。元件的内容会被保存在DFM档案中，并即时重新加载。

**OCaml**
OCaml的标准函式库提供Marshal模组和Pervasives函数，output_value和input_value用于编组。虽然OCaml编程是静态类型检查的，但Marshal模组的使用可能会破坏型别保证，因为没有方法能检查反序列的流，是否代表期望型别的物件。OCaml中的函数或含有函数的资料结构（例如带有方法的物件），由于其中的执行码不可以在相异程式之间传输，所以难以将函数编组。（有一个旗标可标示函数代码的位置，但只能在完全相同的程序中解组）。标准编组功能可以配置一个旗标，来共享和循环资料的处理。

**Smalltalk**
通常，非递回和非共享的物件能利用storeOn:/readFrom:协议，以人类可读的形式来储存和撷取。storeOn:方法产生一个Smalltalk表达式原文，而以readFrom:评估时：重新建立原始物件。这方案特殊之处在于它利用物件的程序描述，而不是资料本身。因此它非常有弹性，允许更紧密的表示类别定义。不过在其原始形式中，它不处理循环的资料结构，也不保留共享参照的识别（即两个参照对应到单一物件，将被恢复为两个相等的参照，但这两份是不同的副本）。
为此，存在各种可携和非可携式的代替方案。其中一些属于特定的Smalltalk实作或是类别馆。在Squeak Smalltalk中有几种方法可以序列化和储存物件。最简单和最常用的是storeOn:/readFrom:，和根基于SmartRefStream二进制储存格式的序列化程序。此外对于包裹物件，可以用ImageSegments来储存和撷取。两者都提供了所谓的“二进制物件仓存框架”，可对紧密二的进制形式执行序列化和撷取。两者都处理循环的、递回的和共享的结构，储存/撷取类别和父类别资讯，并且包括用于“即时”迁移物件的机制（将旧版编写的实例，依照不同物件布局转换成类别）。
这些API（storeBinary/readBinary）虽然彼此相似，但编码细节是不同的，使得这两种格式并不相容。而Smalltalk/X是自由开放源码的，能被加载到其它Smalltalks方言中，允许它们之间能互相交换。物件序列化并非ANSI Smalltalk规范的一部份。因此，序列化物件的代码因Smalltalk实作而异，所得到的二进制资料也不同。例如在Ambrai中就无法恢复在Squeak中所建立的序列化物件。所以，不同Smalltalk实作的各种应用程式，无法在不同实作之间共享资料。这些应用程式包括MinneStore物件资料库和一些RPC包。这个问题的解决方案是SIXX，它是一个使用XML格式进行序列化的Smalltalks的软体包。