serde你在干什么 - deserialize - I

serde 源码解析 | Deserialization篇 第一部分

有了serialize的基础,知道了serde data model大概的定位,也知道了访问者模式的应用,接着理解deserialize会更容易一些。

serde data model为基础,Serialize是引导者的角色,引导Serde::ser::Serilaizer的实现者serializer去参观内存中的数据,帮助serializer完成序列化。

现在反序列化,角色关系更加复杂一层。除了Serde::DeserializeSerde::de::Deserializer,还多一个Serde::de::VisitorSerde::Deserialize依然是最高级引导者,提示Serde::de::Deserializer去认识已被序列化的数据,并且交给它一个Serde::de::Visitor,随后Serde::de::Deserializer则利用得到的Serde::de::Visitor,为其提供数据,帮助它在内存中构建各种数据结构。

有了这个大方向,让我们开始瞅瞅反序列化的过程。

故事开始

也从反序列化的源头开始,也就是from_str

1
2
3
4
5
6
7
8
9
10
11
12
13
// de.rs
pub fn from_str<'a, T>(s: &'a str) -> Result<T>
where
T: Deserialize<'a>,
{
let mut deserializer = Deserializer::from_str(s);
let t = T::deserialize(&mut deserializer)?;
if deserializer.input.is_empty() {
Ok(t)
} else {
Err(Error::TrailingCharacters)
}
}

from_str首先初始化了叫crate::de::Deserializer的结构,该结构实现了serde::de::Deserializer trait,初始化方法是from_str

1
2
3
4
5
6
7
8
9
pub struct Deserializer<'de> {
input: &'de str,
}

impl<'de> Deserializer<'de> {
pub fn from_str(input: &'de str) -> Self {
Deserializer { input }
}
}

从这里我们能得出两个结论:

  1. “本直播间允许套娃”
  2. deserializer拥有反序列化的数据(或数据引用)

然后就将创建的deserializer传给一个Deserialize-thing-thing的写法表示实现某个trait的实例),也就是T,而T在我们编写下列语句时,就已经被推断为Command

1
let de_cmd: Command = from_str(&ser_cmd).unwrap();

所以魔法发生在语句let t = T::deserialize(&mut deserializer)?;里面。

至此我们知道,给T::deserialize一个合适的deserializer,它就会返回一个T类型的实例。

谁才是工具人?

一般来讲,被当作参数传入的,很大可能是工具人,就像序列化过程中写作文的serializer。类比一下,deserializer差不多也是个工具人,供serde::Deserialize差遣。不过,deserializer并不是反序列化中最底层的工具人。最终在内存中复现数据结构的,是一个叫做visitor的工具人,被serde::Deserialize构造,然后交给deserializer使用。

具体一点,T::deserialize要干的事情:

  1. 根据T自身的类型,参照Serde data model,调用deserializer的某个deserialize_*方法,提示deserializer接下来的数据可能是由哪种serde data model序列化而来,你视情况对input做相关的parsing工作,形成一些基础的数据类型。
  2. 调用时,提供一个Visitor-thingdeserializer则调用该visitor的某个visit_*方法,传入经过提示后解析出来的数据,让visitor完成数据结构在内存中的重现。

要明白这个过程,不妨先看Visitor trait的定义,一个visitor,从诞生起,命运就和Value类型绑定在一起,终其一生,都是个不折不扣的工具人,每个方法,都是一条操作规则,比如,如果放一个bool值到visitor眼前,他就如此这般,产生一个Value;如果放一个SeqAccess结构到visitor眼前, 他就这样那样,产生一个ValueVisitor trait的每个规则的默认实现,是抛出Error::invalid_type,表明Value不能由眼前的数据转换而来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
pub trait Visitor<'de>: Sized {
type Value;
fn expecting(&self, formatter: &mut Formatter) -> Result;

fn visit_bool<E>(self, v: bool) -> Result<Self::Value, E>
where
E: Error,
{ ... }
...
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: Error,
{ ... }
...
fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{ ... }
fn visit_map<A>(self, map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{ ... }
fn visit_enum<A>(self, data: A) -> Result<Self::Value, A::Error>
where
A: EnumAccess<'de>,
{ ... }
}

为了更好理解visitor,来看derive_de.rs文件,一部分代码如下,作用是根据数据生成具体的枚举标志符,表示get, set, rm, quit中的哪一个(这么做是为了避免复制为String,提高效率)。这里训练了一个叫做__FieldVisitor的工具人,专门产生__Field类型。一个__Field可以来自于u64、str、bytes,转换逻辑都很直白,match就完事儿了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#[allow(non_camel_case_types)]
enum __Field {
__field0,
__field1,
__field2,
__field3,
}
struct __FieldVisitor;
impl<'de> _serde::de::Visitor<'de> for __FieldVisitor {
type Value = __Field;
fn expecting(
&self,
__formatter: &mut _serde::export::Formatter,
) -> _serde::export::fmt::Result {
_serde::export::Formatter::write_str(__formatter, "variant identifier")
}
fn visit_u64<__E>(self, __value: u64) -> _serde::export::Result<Self::Value, __E>
where
__E: _serde::de::Error,
{
match __value {
0u64 => _serde::export::Ok(__Field::__field0),
1u64 => _serde::export::Ok(__Field::__field1),
2u64 => _serde::export::Ok(__Field::__field2),
3u64 => _serde::export::Ok(__Field::__field3),
_ => _serde::export::Err(_serde::de::Error::invalid_value(
_serde::de::Unexpected::Unsigned(__value),
&"variant index 0 <= i < 4",
)),
}
}
fn visit_str<__E>(self, __value: &str) -> _serde::export::Result<Self::Value, __E>
where
__E: _serde::de::Error,
{
match __value {
"Set" => _serde::export::Ok(__Field::__field0),
"Get" => _serde::export::Ok(__Field::__field1),
"Rm" => _serde::export::Ok(__Field::__field2),
"Quit" => _serde::export::Ok(__Field::__field3),
_ => _serde::export::Err(_serde::de::Error::unknown_variant(
__value, VARIANTS,
)),
}
}
fn visit_bytes<__E>(
self,
__value: &[u8],
) -> _serde::export::Result<Self::Value, __E>
where
__E: _serde::de::Error,
{
match __value {
b"Set" => _serde::export::Ok(__Field::__field0),
b"Get" => _serde::export::Ok(__Field::__field1),
b"Rm" => _serde::export::Ok(__Field::__field2),
b"Quit" => _serde::export::Ok(__Field::__field3),
_ => {
let __value = &_serde::export::from_utf8_lossy(__value);
_serde::export::Err(_serde::de::Error::unknown_variant(
__value, VARIANTS,
))
}
}
}
}

余下的未实现的visit_*就保持默认实现,抛出错误,表明无法完成转换。

现在有了这个工具人,自然就可以为__Field实现Deserialize trait,派出工具人去执行任务,所以上面的代码,紧接着的就是Deserialize trait的实现:

1
2
3
4
5
6
7
8
9
impl<'de> _serde::Deserialize<'de> for __Field {
#[inline]
fn deserialize<__D>(__deserializer: __D) -> _serde::export::Result<Self, __D::Error>
where
__D: _serde::Deserializer<'de>,
{
_serde::Deserializer::deserialize_identifier(__deserializer, __FieldVisitor)
}
}

故事开始时,我们知道,给T::deserialize一个合适的deserializer,它就会返回一个T类型的实例。所以为__Field实现Deserialize trait的意思就是,给__Field写一个deserialize方法,保证调用__Field::deserialze(&mut deserializer)之后,会返回一个__Field::__field0或者__filed1等等。

我们发现,具体的实现其实非常简单,就是调用传入的deserializerdeserialize_identifier方法,并且为其提供刚才构造的__FieldVisitor

deserialize_identifier在这儿被使用,因为我们知道__Field的实质,是结构体字段、枚举的标识。所以我们提示deserializer我们需要的是一个标识,具体deserializer怎么处理,不同的数据格式都有自己的说法。比如这里的json知道,哦,标识啊,当初序列化时就直接把它当成字符串对待了,所以接下来的input数据,应该会以字符串开头,所以deserializer直接交给字符串的反序列化去处理。

1
2
3
4
5
6
fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
self.deserialize_str(visitor)
}

deserialize_str的实现中,主要做了两个工作:

  1. 把接下来的input数据解析出一个字符串,也就是期待input以引号开头,"xxx",解析出xxx,如果顺利,xxx应该是Set、Get、Rm、Quit中的一个。
  2. 既然解析出了&str类型的数据,那么就应该期待,visitor可以拿这个&str去构建一个__Field实例。所以deserializer_str就会使用之前__FieldVisitorvisit_str方法,让工具人自己去处理。这里实际调用的visit_borrowed_str,其默认实现就是调用visit_str。分得如此细,和生命周期、性能等有关,这里暂不关注。visit_str方法自然会根据xxx返回相应的__Field::__fieldx
1
2
3
4
5
6
fn deserialize_str<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
visitor.visit_borrowed_str(self.parse_string()?)
}

到这里其实已经看到了一个局部相对完整的序列化过程,但也只反序列化了一个标识符。下面会再看我们Command到底是怎么构建的,过程的骨架其实也就这样了。

有个题外话,注意这里从数据中解析出str的函数,parse_string,也由deserializer实现,但是官方有提醒,Serde框架内,实际上并不包含解析,更有效率的方式是使用专门的解析库来帮助实现deserializer

// SERDE IS NOT A PARSING LIBRARY. This impl block defines a few basic parsing
// functions from scratch. More complicated formats may wish to use a dedicated
// parsing library to help implement their Serde deserializer

Command的反序列化

因为我们想要反序列化Command,所以给Command也要整一个visitor,其结构为被命名为__Visitor。看上去这个visitor有两个字段,markerlifetimePhantomData类型,看上去就很厉害,但是我没找到使用这个两个字段的代码,应该对我们理解总体逻辑没有太多阻碍,所以这里暂时跳过吧。(实际上我至今还是没有理解这个marker的作用是啥,源码中也没有相关的注释)

__Visitor实现Visitor trait 的这一段代码看上去很长,有点唬人,但其实是一个match+嵌套,所以先撇开嵌套的内容,看主体的match。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
struct __Visitor<'de> {
marker: _serde::export::PhantomData<Command>,
lifetime: _serde::export::PhantomData<&'de ()>,
}
impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> {
type Value = Command;
fn expecting(&self, __formatter: &mut _serde::export::Formatter) -> _serde::export::fmt::Result {
_serde::export::Formatter::write_str(__formatter, "enum Command")
}
// Command由“代表枚举的数据”转换而来
fn visit_enum<__A>(self, __data: __A) -> _serde::export::Result<Self::Value, __A::Error>
where
__A: _serde::de::EnumAccess<'de>,
{
// 重点步骤,这里解析出数据具体是哪个枚举,后文会再回顾这里的操作
match match _serde::de::EnumAccess::variant(__data) {
_serde::export::Ok(__val) => __val,
_serde::export::Err(__err) => {
return _serde::export::Err(__err);
}
} {
(__Field::__field0, __variant) => {
// 开始嵌套,如果是Set类型,就再利用Field模式,解析Key、Value的identifier
// 然后再新造一个Set的visitor,利用后续的数据构建Command::Set
}
(__Field::__field1, __variant) => { // Get基本步骤同Set }
(__Field::__field2, __variant) => {...}
(__Field::__field3, __variant) => {...}
}
}
}

所以Commandvisitor的透露的大体信息是,deserializer只有给我提供一个EnumAccess<'de>-thing这种数据,我才能构建一个Command,具体的做法,就是利用之前的__Field相关结构,判断是具体哪种Command后,再采用不同的策略构建。这里先不深究细节,我们接着走流程。

Commandvisitor整好了,也有相似的入口来使用,Command通过deserialize_enum提示deserializer,接下来的数据应该是为enum准备,你看着办,顺便告诉你,这个enum的名字是"Command”,枚举的项目是Set、Get、Rm、Quit四种。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#[automatically_derived]
impl<'de> _serde::Deserialize<'de> for Command {
fn deserialize<__D>(__deserializer: __D) -> _serde::export::Result<Self, __D::Error>
where
__D: _serde::Deserializer<'de>,
{
const VARIANTS: &'static [&'static str] = &["Set", "Get", "Rm", "Quit"];
_serde::Deserializer::deserialize_enum(
__deserializer,
"Command",
VARIANTS,
__Visitor {
marker: _serde::export::PhantomData::<Command>,
lifetime: _serde::export::PhantomData,
},
)
}
}

deserialize_enum是怎么实现的呢?json格式的deserializer被提示接下来的数据是为enum准备的之后,立刻明白,一个enum,经过合法序列化后,只能由"或者{开头,所以分两种情况,围绕自己的input数据,为visitor的创建提供基础材料。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
fn deserialize_enum<V>(
self,
_name: &'static str,
_variants: &'static [&'static str],
visitor: V,
) -> Result<V::Value>
where
V: Visitor<'de>,
{
if self.peek_char()? == '"' {
// Visit a unit variant.
visitor.visit_enum(self.parse_string()?.into_deserializer())
} else if self.next_char()? == '{' {
// Visit a newtype variant, tuple variant, or struct variant.
let value = visitor.visit_enum(Enum::new(self))?;
// Parse the matching close brace.
if self.next_char()? == '}' {
Ok(value)
} else {
Err(Error::ExpectedMapEnd)
}
} else {
Err(Error::ExpectedEnum)
}
}

提供什么样的材料呢?这要看deserializer具体选用visitor的哪个方法。这里使用的visit_enum方法(当然__Visitor也只实现了visit_enum方法),所以按照其签名,应该为其提供一个实现EnumAccess trait的东西。

  • self.parse_string()?.into_deserializer()提供的流程是,分析出str,经过into_deserializer()变为StrDeserializer,而一个StrDeserializer默认实现EnumAccess

  • Enum::new(self)提供的流程就很直接,自定义了一个实现EnumAccess的结构叫做Enum

所以到这里,上一节分析的”工具人套工具人”的骨架就分析完了。复习一下, 总体过程是,Command::deserialize通过调用deserialize_enum方法提示deserializer,接下来的序列化数据是为enum准备的,并且为deserializer提供一个实现了visit_enum(接受EnumAccess-thing,返还Command)的visitor。于是deserializer根据json的规则知道,接下的序列化数据应该会是"或者{开头,分为两种情况,制作出一个EnumAccess-thing,然后交给visit_enum去构建Command

这时,我们更详细地去看这个过程,visit_enum是怎么做的,EnumAccess-thing又是如何与它配合的。

visit_enum里

为了方便叙述,把visit_enum再抄到这里

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fn visit_enum<__A>(self, __data: __A) -> _serde::export::Result<Self::Value, __A::Error>
where
__A: _serde::de::EnumAccess<'de>,
{
match match _serde::de::EnumAccess::variant(__data) {
_serde::export::Ok(__val) => __val,
_serde::export::Err(__err) => {
return _serde::export::Err(__err);
}
} {
(__Field::__field0, __variant) => {...},
(__Field::__field1, __variant) => {...},
(__Field::__field2, __variant) => {...},
(__Field::__field3, __variant) => {...},
}
}

同时也附上EnumAccess trait的定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// https://docs.serde.rs/serde/de/trait.EnumAccess.html
pub trait EnumAccess<'de>: Sized {
type Error: Error;
type Variant: VariantAccess<'de, Error = Self::Error>;
fn variant_seed<V>(
self,
seed: V
) -> Result<(V::Value, Self::Variant), Self::Error>
where
V: DeserializeSeed<'de>;

fn variant<V>(self) -> Result<(V, Self::Variant), Self::Error>
where
V: Deserialize<'de>,
{
// 默认实现
self.variant_seed(PhantomData)
}
}

一开始,对EnumAccess-thing调用variant方法,它返回一个tuple,从后续match分支可以推断,第一项是之前定义的__Field,第二项,从variant方法的签名可知,是VariantAccess-thing。自然地,通过匹配__Field,就可以知道数据本身是Set、Get、Rm、Quit中的哪一个,然后继续利用产生的VariantAccess-thing做继续的处理……但是,等一等,这是怎么办到的?什么时候利用数据反序列化出了一个__Field__Field::deserialize在哪儿被偷偷摸摸地调用的了?

要解决这个疑问,自然要看variant的内部,所以自然要看具体的EnumAccess实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// de.rs
struct Enum<'a, 'de: 'a> {
de: &'a mut Deserializer<'de>,
}

impl<'a, 'de> Enum<'a, 'de> {
fn new(de: &'a mut Deserializer<'de>) -> Self {
Enum { de }
}
}

impl<'de, 'a> EnumAccess<'de> for Enum<'a, 'de> {
type Error = Error;
type Variant = Self;

fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant)>
where
V: DeserializeSeed<'de>,
{
let val = seed.deserialize(&mut *self.de)?;
if self.de.next_char()? == ':' {
Ok((val, self))
} else {
Err(Error::ExpectedMapColon)
}
}
}

我的第一反应,自然是找__Field::deserialize方法。但是只找到一个seed.deserialize,那么不负责任地一猜,这个seed应该是__Field的一个实例,但是被当场打脸,因为由variant传入的,又是个PhantomData,所以这个东西是铁定绕不过了?还有那个variant_seed方法中要求的DeserializeSeed又是什么东西?这个过程到底怎么回事?

为了控制一下篇幅,这些疑问下一篇继续解答。

到这里,我们其实已经收获颇丰,知道了visitor的存在,也大概清楚了三种角色的交互逻辑。后续的问题,实际上是反序列化一些集合类型时的所需要的细节。继续之前,先休息一下。