serde 源码解析 | Deserialization篇 第一部分
有了serialize的基础,知道了serde data model
大概的定位,也知道了访问者模式的应用,接着理解deserialize会更容易一些。
以serde data model
为基础,Serialize
是引导者的角色,引导Serde::ser::Serilaizer
的实现者serializer
去参观内存中的数据,帮助serializer
完成序列化。
现在反序列化,角色关系更加复杂一层。除了Serde::Deserialize
,Serde::de::Deserializer
,还多一个Serde::de::Visitor
。Serde::Deserialize
依然是最高级引导者,提示Serde::de::Deserializer
去认识已被序列化的数据,并且交给它一个Serde::de::Visitor
,随后Serde::de::Deserializer
则利用得到的Serde::de::Visitor
,为其提供数据,帮助它在内存中构建各种数据结构。
有了这个大方向,让我们开始瞅瞅反序列化的过程。
故事开始 也从反序列化的源头开始,也就是from_str
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
。
pub struct Deserializer <'de > { input: &'de str , }impl <'de > Deserializer<'de > { pub fn from_str (input: &'de str ) -> Self { Deserializer { input } } }
从这里我们能得出两个结论:
“本直播间允许套娃”
deserializer
拥有反序列化的数据(或数据引用)
然后就将创建的deserializer
传给一个Deserialize-thing
(-thing
的写法表示实现某个trait的实例),也就是T,而T在我们编写下列语句时,就已经被推断为Command
。
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
要干的事情:
根据T自身的类型,参照Serde data model ,调用deserializer
的某个deserialize_*
方法,提示deserializer
接下来的数据可能是由哪种serde data model
序列化而来,你视情况对input做相关的parsing工作,形成一些基础的数据类型。
调用时,提供一个Visitor-thing
,deserializer
则调用该visitor
的某个visit_*
方法,传入经过提示后解析出来的数据,让visitor
完成数据结构在内存中的重现。
要明白这个过程,不妨先看Visitor trait的定义,一个visitor
,从诞生起,命运就和Value
类型绑定在一起,终其一生,都是个不折不扣的工具人,每个方法,都是一条操作规则,比如,如果放一个bool
值到visitor
眼前,他就如此这般,产生一个Value
;如果放一个SeqAccess
结构到visitor
眼前, 他就这样那样,产生一个Value
。Visitor
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的实现:
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
等等。
我们发现,具体的实现其实非常简单,就是调用传入的deserializer
的 deserialize_identifier
方法,并且为其提供刚才构造的__FieldVisitor
。
deserialize_identifier
在这儿被使用,因为我们知道__Field
的实质,是结构体字段、枚举的标识。所以我们提示 deserializer
我们需要的是一个标识,具体deserializer
怎么处理,不同的数据格式都有自己的说法。比如这里的json知道,哦,标识啊,当初序列化时就直接把它当成字符串对待了,所以接下来的input数据,应该会以字符串开头,所以deserializer
直接交给字符串的反序列化去处理。
fn deserialize_identifier <V>(self , visitor: V) -> Result <V::Value>where V: Visitor<'de >, { self .deserialize_str(visitor) }
而deserialize_str
的实现中,主要做了两个工作:
把接下来的input数据解析出一个字符串,也就是期待input以引号开头,"xxx"
,解析出xxx
,如果顺利,xxx
应该是Set、Get、Rm、Quit
中的一个。
既然解析出了&str类型的数据,那么就应该期待,visitor
可以拿这个&str去构建一个__Field
实例。所以deserializer_str
就会使用之前__FieldVisitor
的visit_str
方法,让工具人自己去处理。这里实际调用的visit_borrowed_str
,其默认实现 就是调用visit_str
。分得如此细,和生命周期、性能等有关,这里暂不关注。visit_str
方法自然会根据xxx
返回相应的__Field::__fieldx
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有两个字段,marker
和lifetime
,PhantomData
类型,看上去就很厉害,但是我没找到使用这个两个字段的代码,应该对我们理解总体逻辑没有太多阻碍,所以这里暂时跳过吧。(实际上我至今还是没有理解这个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" ) } 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) => {...} } } }
所以Command
的visitor
的透露的大体信息是,deserializer
只有给我提供一个EnumAccess<'de>-thing
这种数据,我才能构建一个Command
,具体的做法,就是利用之前的__Field
相关结构,判断是具体哪种Command
后,再采用不同的策略构建。这里先不深究细节,我们接着走流程。
Command
的visitor
整好了,也有相似的入口来使用,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()? == '"' { visitor.visit_enum(self .parse_string()?.into_deserializer()) } else if self .next_char()? == '{' { let value = visitor.visit_enum(Enum::new(self ))?; if self .next_char()? == '}' { Ok (value) } else { Err (Error::ExpectedMapEnd) } } else { Err (Error::ExpectedEnum) } }
提供什么样的材料呢?这要看deserializer
具体选用visitor
的哪个方法。这里使用的visit_enum
方法(当然__Visitor
也只实现了visit_enum
方法),所以按照其签名,应该为其提供一个实现EnumAccess
trait的东西。
所以到这里,上一节分析的”工具人套工具人”的骨架就分析完了。复习一下, 总体过程是,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 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 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的存在,也大概清楚了三种角色的交互逻辑。后续的问题,实际上是反序列化一些集合类型时的所需要的细节。继续之前,先休息一下。