serde你在干什么 - deserialize - II

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

上篇,已经把反序列化的总体过程描述了一遍,也留下了几个问题,DeserializeSeedPhantomData是什么,EnumAccess中的variant方法被visit_enum调用后,为什么能产生一个__Field实例呢?

DeserializeSeed

1
2
3
4
5
6
7
8
9
10
11
12
pub trait DeserializeSeed<'de>: Sized {
type Value;
fn deserialize<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
where
D: Deserializer<'de>;
}

pub trait Deserialize<'de>: Sized {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>;
}

Deserialize的区别,就在于用DeserializeSeed自定义了输出的类型,不必和实现类型一致。

也就是说,对一个DeserializeSeed-thing调用它的deserialize去消费一个deserializer提供的数据,可以返回的是自定义的Value类型。

回忆通常情况下,对Deserialize的使用,是利用泛型参数,由Result<T>推断出具体的T类型,再调用对应的deserialze方法,返回一个T类型的实例,这个过程只有类型参与,属于无状态的反序列化,比如下面的T::deserialize()

1
2
3
4
5
6
7
8
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)?;
Ok(t)
}

但是对于实现了DeserializeSeed的类型,使用上有所不同,通常是初始化成为一个实例,可以拥有自己的数据,常被当作参数seed传入一个形如fn *_seed()的方法,由它调用seed上的反序列化方法,比如下面的seed.deserialize(),这个deserialize过程则允许seed`自身数据参与,所以是个有状态的反序列化过程。

1
2
3
4
5
6
7
8
9
10
11
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)
}
}

不妨再来看看,哪些地方存在这样的fn *_seed():

  • SeqAccess中的next_element_seed

  • MapAccess中的next_key_seednext_value_seednext_entry_seed

  • EnumAccess中的variant_seed

  • VariantAcess中的newtype_variant_seed

这些方法出现在deserializervisitor提供的集合数据类型中。一个visitor,只有在实现了visit_seqvisit_mapvisit_enum这类方法的前提下,才有机会使用有状态的反序列化。

但在实际工作中,大多数情况的反序列化,都是无状态的,所以给这些fn *_seed()方法传递的DeserializeSeed-thing,也就是参数seed,自然可以没有自己的数据,仅仅把某种类型信息给传递进去。像这种有类型、却没有实际数据的东西,标准库里专门有个类型叫PhantomData,幽灵数据来表示,编译器不会给他分配内存空间,只用来做类型的推断和方法的关联。

Serde已经为PhantomData实现DeserializeSeed,使其成为DeserializeSeed-thing,直接把PhantomData传入fn *_seed()里,就等效于无状态Deserialize的过程,只有类型参与。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
impl<'de, T> DeserializeSeed<'de> for PhantomData<T>
where
T: Deserialize<'de>,
{
type Value = T;

#[inline]
fn deserialize<D>(self, deserializer: D) -> Result<T, D::Error>
where
D: Deserializer<'de>,
{
// 方法调用PhantomData的deserialize,最后落脚到这里,等于绕了一圈确定T类型
T::deserialize(deserializer)
}
}

甚至,因为无状态反序列化太普遍,Serde提供了一种快捷方式,比如next_element_seed的无状态化版本是next_element,就直接帮你传入了PhantomData,使用next_element时,T就通过表达式左值的类型来推断就OK了。

1
2
3
4
5
6
fn next_element<T>(&mut self) -> Result<Option<T>, Self::Error>
where
T: Deserialize<'de>,
{
self.next_element_seed(PhantomData)
}

有了这样的基础,再看visit_enum就清楚了,中间调用match _serde::de::EnumAccess::variant(__data)时,推断出T的类型是__Field,也就是使用的variant版本如下:

1
2
3
4
5
6
fn variant<__Field>(self) -> Result<(__Field, Self::Variant), Self::Error>
where
__Field: Deserialize<'de>,
{
self.variant_seed(PhantomData)
}

因此PhantomDataPhantomData<__Field>的一个“实例”,实现了DeserializeSeed,传到variant_seed中叫做seed,根据PhantomData默认的DeserializeSeed的实现,variant_seed中语句seed.deserialize(deserializer)的本质就是__Field::deserialize(deserializer),于是产生了具体的__Field值。

嵌套之路

清楚了__Field的来历之后,不妨再关注一下tuple中的第二项,VariantAccess-thing,它包含着四个必须实现的方法,概括了可能出现的枚举形态

  • unit_variant,比如对应Command::Quit
  • newtype_variant,比如对应Command::Rm
  • tuple_variant,比如对应Command::SomeCmd(a, b, c)这种
  • struct_variant,比如对应Command::{Get, Set}

一个VariantAccess可以看做特定领域的deserializer,这个四个方法和通用的deserialize_*方法的作用类似,由上层调用,提示接下来的数据应该可以按照什么样的规则去解析。所以这里的VisitorVariantAccess功能上如同一对DeserializeDeserializer,这不就是一层嵌套吗?

举两个例子,这两个简单的match分支,Command::QuitCommand::Rm ,就分别提示接下来按照unit_variatnewtype_variant的规则解析数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
// derive_de.rs
(__Field::__field2, __variant) => {
_serde::export::Result::map(_serde::de::VariantAccess::newtype_variant::<String>(__variant), Command::Rm)
}
(__Field::__field3, __variant) => {
match _serde::de::VariantAccess::unit_variant(__variant) {
_serde::export::Ok(__val) => __val,
_serde::export::Err(__err) => {
return _serde::export::Err(__err);
}
};
_serde::export::Ok(Command::Quit)
}

但是复杂一点的Command::Set,它调用的是struct_variant,表明接下来期待的是结构性的枚举。到这里,更明显的嵌套出现了,又新建一个__Field类型,用来保存Command::Get中的KeyValue的标识符信息。然后派出一个visitor,可以从SeqAccess或者MapAccess类型的数据来构建Command::Get的实例。

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
67
68
69
70
71
72
73
74
75
76
77
78
79
// derive_de.rs

#[allow(non_camel_case_types)]
enum __Field { __field0, __field1, __ignore, }
struct __FieldVisitor;
impl<'de> _serde::de::Visitor<'de> for __FieldVisitor {
type Value = __Field;
fn expecting(&self, __formatter: &mut _serde::export::Formatter)
fn visit_u64<__E>(self, __value: u64)
fn visit_str<__E>(self, __value: &str)
fn visit_bytes<__E>(self, __value: &[u8])
}
impl<'de> _serde::Deserialize<'de> for __Field { ... }

// 再造一个Command::Set的visitor
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, "struct variant Command::Set")
}
// Set由Seq生成
#[inline]
fn visit_seq<__A>(self, mut __seq: __A) -> _serde::export::Result<Self::Value, __A::Error>
where
__A: _serde::de::SeqAccess<'de>,
{
// 反序列化两个字段,类型为String,由最后的构建函数推断
let __field0 = match match _serde::de::SeqAccess::next_element::<String>(&mut __seq) {
_serde::export::Ok(__val) => __val,
_serde::export::Err(__err) => {
return _serde::export::Err(__err);
}
} {
_serde::export::Some(__value) => __value,
_serde::export::None => {
return _serde::export::Err(_serde::de::Error::invalid_length(
0usize,
&"struct variant Command::Set with 2 elements",
));
}
};
let __field1 = match match _serde::de::SeqAccess::next_element::<String>(&mut __seq) {
_serde::export::Ok(__val) => __val,
_serde::export::Err(__err) => {
return _serde::export::Err(__err);
}
} {
_serde::export::Some(__value) => __value,
_serde::export::None => {
return _serde::export::Err(_serde::de::Error::invalid_length(
1usize,
&"struct variant Command::Set with 2 elements",
));
}
};
// 一个Set成功返回
_serde::export::Ok(Command::Set {
key: __field0,
value: __field1,
})
}
// 还可以从map生成,省略
#[inline]
fn visit_map<__A>(self, mut __map: __A)
}
// 使用struct_variant提示,尝试解析出一个struct的枚举,也就是Set
const FIELDS: &'static [&'static str] = &["key", "value"];
_serde::de::VariantAccess::struct_variant(
__variant,
FIELDS,
__Visitor {
marker: _serde::export::PhantomData::<Command>,
lifetime: _serde::export::PhantomData,
},
)

因为json中struct和字典表达形式相同,并且key值都包含在序列化数据中,所以不必使用提供的_fields参数,直接交给deserialize_map就足够。

1
2
3
4
5
6
7
// de.rs
fn struct_variant<V>(self, _fields: &'static [&'static str], visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
de::Deserializer::deserialize_map(self.de, visitor)
}

deserialize_map自然检查map的开闭标识,然后给予一个实现了MapAccess的结构。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
fn deserialize_map<V>(mut self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
// 字典由{开启
if self.next_char()? == '{' {
// 调用 visit_map,所以给一个MapAccess,由CommaSeparated实现
let value = visitor.visit_map(CommaSeparated::new(&mut self))?;
// 字典由}结束
if self.next_char()? == '}' {
Ok(value)
} else {
Err(Error::ExpectedMapEnd)
}
} else {
Err(Error::ExpectedMap)
}
}

后面的事情,就是CommaSeparatedvisit_map相互配合产生Command::Get了,过程类似Enumvisit_enum的交互,这里就不再详细一步步跟踪了。

尾声

我们用两章的篇幅介绍了反序列化过程。

第一个关键点,明确角色

  • Deserialize观察目标类型,制作Visitor,调用Deserializer,交付Visitor
  • Deserialzier和被序列化数据打交道,根据Deserialize的提示,把序列化数据解析成恰当的基础数据结构,交给Visitor
  • Visitor根据Deserializer给的基础数据类型,按既定规则生成目标类型

第二个关键点,集合类型的反序列化

Deserializer被提示接下来的数据可能是由集合类型序列化而来时,所谓的恰当的基础数据结构,就是实现了*Access系列 trait 的结构,代表着某一种特定结构的Deserializer。其实也就只有四个。

Trait Description
EnumAccess 重要方法是variant,一次性调用,返回(T, VariantAccess),判断数据属于哪种枚举
VariantAccess 四个必须方法,一次性调用,解析具体的某种枚举
MapAccess 按照map的风格访问处理已序列化数据,next_keynext_value
SeqAccess 按照seq的风格访问处理已序列化数据,next_element

他们统一的风格就是都使用了fn *_seed()的模式,通过DeserializerSeed,提供有状态反序列化的能力。普通的反序列化场景,往往不需要有状态,因此传入PhantomData,直接使用无状态反序列化。

Ok,反序列化也介绍到这里吧。如果想要进一步巩固serde,自己写一个data format的DeserializerSerializer吧。下一篇,我们为redis协议写一个基础的序列化工具。