zl程序教程

您现在的位置是:首页 >  后端

当前栏目

serde - rust的序列化方案

rust 方案 序列化
2023-09-27 14:20:16 时间

原文: Overview · Serde
翻译: kula

serde, 是rust语言用来序列化和反序列化数据的一个非常高效的解决方案。

本质上,serde提供了一个序列化层的概念。可以将任何支持的数据格式反序列化成中间格式,然后序列化成任何一种支持的数据格式。

设计

同其他语言通过反射来实现序列化方案不同,serde基于rust的trait系统来实现自己的序列化,每种数据结构通过实现serde的Serialize和Deserialize接口来实现序列化功能,这种实现方案可以有效避免反射的性能开销。rust编译器可以在很多场景下对序列化进行高度优化,甚至可以达到一个资深工程师手写序列化代码的性能。

支持的数据格式

这里列举一下目前serde社区已经实现的一些数据格式。

  • json
  • bincode
  • CBOR
  • YAML
  • MessagePack
  • TOML
  • Pickle
  • RON
  • BSON
  • Avro
  • Hjson
  • JSON5
  • URL
  • Envy
  • Envy Store

数据结构

serde支持以下数据类型的序列化和反序列化,包括(String,&str,usize,Vec,HashMap<K,V>)。此外,serde还提供derive macro来为你自己定义的数据类型提供序列化和反序列化的实现。下面给出dervie macro的demo code。

#[macro_use]
extern crate serde_derive;

extern crate serde;
extern crate serde_json;

#[derive(Serialize, Deserialize, Debug)
struct Point {
    x: i32,
    y: i32,
}

fn main() {
    let point = Point { x: 1, y: 2 };

    // Convert the Point to a JSON string.
    let serialized = serde_json::to_string(&point).unwrap();

    // Prints serialized = {"x":1,"y":2}
    println!("serialized = {}", serialized);

    // Convert the JSON string back to a Point.
    let deserialized: Point = serde_json::from_str(&serialized).unwrap();

    // Prints deserialized = Point { x: 1, y: 2 }
    println!("deserialized = {:?}", deserialized);
}

serde数据模型

serde数据模型就是一组数据结构和数据格式转换的api,你可以理解为serde的类型系统。

serde序列化的部分定义在Serializer的trait里。反序列化的部分定义在 Deserializer的trait里。Serde提供了一种方法,将任何Rust的数据类型映射成29种可能的类型。 Serializer trait的每一种方法都对应数据模型的每一种类型。

当将一个数据结构序列化成某一种数据格式时,该数据结构里Serializer的实现将数据结构通过Serializer方法精确的映射成Serde数据模型。而Serializer自己的实现再将Serde数据模型映射成输出的数据格式。

当从某一种数据格式反序列化成rust数据结构时,Deserializer自己的实现会把数据格式映射成Serde数据模型,再通过数据结构里Deserializer的实现,将Serde数据模型通过Visitor实现映射成rust数据结构。

serde类型系统

serde数据模型是rust类型系统的一个简化版本。它包括以下29种类型。

bool
i8, i16, i32, i64, i128
u8, u16, u32, u64, u128
f32, f64
char
string
bytearray
option
uint
uint_struct
uint_variant0
newtype_struct
newtype_variant
seq
tuple
tuple_struct
tuple_variant
map
struct
struct_variant

数据模型映射

在很多场景下, 把rust数据结构映射成serde数据模型是非常快速的。例如rust的bool类型映射成serde的bool类型,rust的tuple struct Rgb(u8, u8, u8)映射成serde的tuple struct结构。

Serialize和Deserialize trait可以将rust数据结构和serde数据模型快速映射起来。

举个栗子,考虑一下Rust的std::ffi::OsString类型,这个类型表示一个平台相关的字符串。在Unix系统上,他表示非零字节。 在windows系统上,他表示非零16位值。

我们可以很自然的想到将OsString映射成serde的以下两种类型。

作为serde字符串类型。 但里面是存在坑的。 因为OsString是非零数据,但serde字符串是utf8展示的, 允许字符串里存在零字节。这里在转换的时候会遇到问题。
作为serde字节数组类型。 这里没有作为serde字符串类型的坑。但是存在新的坑, 就是如果我们在Unix下序列化OsString类型,然后在windows下反序列化。因为这两个平台对字符串的定义不一致, 所以在这个转换中就会出现错误, 我们会得到一个不正常的反序列化之后的字符串。
所以大家看,做一个序列化和反序列化的方案其实不是那么容易的。好在我们还可以用一种方式来实现这个目的,那就是不要把OsString类型看做是一个字符串或者是一个字节数组。 我们还可以把它看成一个枚举。我们可以用如下的类型来定义它,哪怕跟每个平台的定义都不匹配。

enum OsString {
    Unix(Vec<u8>),
    Windows(Vec<u16>),
    // and other platforms
}

映射到serde数据类型的方法灵活性是非常高的,在实现Serialize和Deserialize的过程中,一定要注意场景的上下文,根据场景选择最合适的方案来做。

使用derive

为了方便实现你自己创建的数据结构的序列化功能,serde提供了一个derive macro来自动生成你的数据结构的Serialize和Deserialize trait的实现代码。让你定义的数据结构通过serde数据模型方便的表达出来。

你只需要在你代码里通过添加一行#[derive(Serialize,Deserialize)] 。serde就会自动生成这些实现代码。

此功能基于rust的#[derive]范式来实现,类似于你通过derive自动实现系统内置的Clone,Copy,Debug和其他的一些trait。他能够给绝大多数数据结构和枚举自动生成trait实现代码。极少数情况下,面对一些非常复杂的数据结构,你可能需要手工去实现这些代码。