一次往返
主线位置:序列化主线和反序列化主线各从头走到尾一遍。前半页是一次
serde_json::to_string:derive 生成的代码把数据推给Serializer;后半页是一次serde_json::from_str:serde_json 读字节、回调 derive 生成的Visitor。每一步只讲到「谁调了谁」,trait 本身在 02 核心 trait,生成代码的细节在 生成的 Serialize 和 生成的 Deserialize。
sequenceDiagram participant U as 调用方 participant P as Post 的 impl participant J as serde_json U->>J: to_string(post) J->>P: serialize(ser) P->>J: serialize_struct P->>J: serialize_field 三次 P->>J: end J-->>U: 字符串 U->>J: from_str(s) J->>P: deserialize(de) P->>J: deserialize_struct(visitor) J->>P: visit_map(MapAccess) P->>J: next_key 与 next_value J-->>U: Post
例子
类型和上一页serde 解决什么问题里的一样:一个 struct,字段里有一个 String、一个 Vec<String> 和一个 enum。enum 一个变体不带数据,一个带一个 u32,覆盖了数据模型里的 struct、string、seq、unit_variant、newtype_variant、u32。
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub struct Post {
pub title: String,
pub tags: Vec<String>,
pub status: Status,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub enum Status {
Draft,
Published(u32),
}
src/bin/round_trip.rs:
use exp_serde_overview::model::{Post, Status};
fn main() {
let post = Post {
title: "serde".to_string(),
tags: vec!["rust".to_string(), "json".to_string()],
status: Status::Published(2026),
};
let json = serde_json::to_string(&post).unwrap();
println!("{json}");
let back: Post = serde_json::from_str(&json).unwrap();
println!("{back:?}");
println!("equal: {}", back == post);
let draft: Status = serde_json::from_str(r#""Draft""#).unwrap();
println!("{draft:?} -> {}", serde_json::to_string(&draft).unwrap());
}
M2 Max、macOS、rustc 1.99.0,serde =1.0.229、serde_json =1.0.154:
{"title":"serde","tags":["rust","json"],"status":{"Published":2026}}
Post { title: "serde", tags: ["rust", "json"], status: Published(2026) }
equal: true
Draft -> "Draft"
带数据的变体写成 {"Published":2026},不带数据的写成字符串 "Draft"。这是 serde 默认的外部标签表示,其他表示在 04 enum 的表示与缓冲。
生成的 Serialize
cargo expand --lib model 展开 Post 的 Serialize(只摘函数体,去掉了外层的 const _: () = { extern crate serde as _serde; ... } 包装和属性):
impl _serde::Serialize for Post {
fn serialize<__S>(
&self,
__serializer: __S,
) -> _serde::__private229::Result<__S::Ok, __S::Error>
where
__S: _serde::Serializer,
{
let mut __serde_state = _serde::Serializer::serialize_struct(
__serializer,
"Post",
false as usize + 1 + 1 + 1,
)?;
_serde::ser::SerializeStruct::serialize_field(
&mut __serde_state,
"title",
&self.title,
)?;
_serde::ser::SerializeStruct::serialize_field(
&mut __serde_state,
"tags",
&self.tags,
)?;
_serde::ser::SerializeStruct::serialize_field(
&mut __serde_state,
"status",
&self.status,
)?;
_serde::ser::SerializeStruct::end(__serde_state)
}
}
三步:serialize_struct 拿到一个状态对象 __serde_state,对每个字段调一次 serialize_field(键, &值),最后 end。生成这段代码的模板是 serde_derive 的 serialize_struct_as_struct,核心就是一个 quote_block!:
quote_block! {
let #let_mut __serde_state = _serde::Serializer::serialize_struct(__serializer, #type_name, #len)?;
#tag_field
#(#serialize_fields)*
_serde::ser::SerializeStruct::end(__serde_state)
}
#serialize_fields 由 serialize_struct_visitor 逐字段生成;#len 是一个 fold 出来的表达式,起点是 #tag_field_exists as usize,每个字段加 1(带 skip_serializing_if 的字段加 if 条件 { 0 } else { 1 })。所以展开结果里是 false as usize + 1 + 1 + 1 而不是 3,编译器会把它折成常量。
false as usize 是修一个 flatten 丢标签的 bug 留下的#1474(「Fixed #1468, flattened struct fields made structs ignore their tag」,首次发布在 1.0.87)之前,字段数的起点是一个整数字面量 additional_field_count:#[serde(tag = "...")] 的 struct 是 1,否则是 0,标签字段也只在 serialize_struct_as_struct 里插进去。#1468 报告的是:struct 带了 tag,又有一个 #[serde(flatten)] 字段,输出里就没有标签了,因为带 flatten 的 struct 走的是另一个函数 serialize_struct_as_map,那里没有插标签的代码。修复把插标签抽成 serialize_struct_tag_field,两条路径都调它,字段数的起点也统一改成 quote!(#tag_field_exists as usize)。PR 描述说这次也顺手「overhauls my initial implementation a bit to make it a bit cleaner an easier to read」;1.0.87 的 release notes 写的是「Fix inclusion of tag in struct containing flattened fields (#1468)」。1.0.229 里没有标签的 struct 展开出来就是 false as usize + ...。
enum 的展开是一个 match,每个变体一个分支,模板是 serialize_externally_tagged_variant:
match *self {
Status::Draft => {
_serde::Serializer::serialize_unit_variant(
__serializer,
"Status",
0u32,
"Draft",
)
}
Status::Published(ref __field0) => {
_serde::Serializer::serialize_newtype_variant(
__serializer,
"Status",
1u32,
"Published",
__field0,
)
}
}
类型名、变体序号、变体名一起交出去,格式自己挑(上一页的 bincode 只用了序号)。
序列化主线:一次 to_string
to_string 调 to_vec,to_vec 先 Vec::with_capacity(128),再调 to_writer:
pub fn to_writer<W, T>(writer: W, value: &T) -> Result<()>
where
W: io::Write,
T: ?Sized + Serialize,
{
let mut ser = Serializer::new(writer);
value.serialize(&mut ser)
}
Serializer::new 用的是 CompactFormatter。实现 serde::Serializer 的是 &mut Serializer<W, F>,不是 Serializer<W, F> 本身:trait 的方法都按值拿 self,传一个可变引用进去,嵌套的值递归调用时可以反复 &mut *self 借出来。to_string 最后用 String::from_utf8_unchecked 把字节转成字符串,注释写着「We do not emit invalid UTF-8.」。
打印每一次调用
要看清 Post::serialize 和 serde_json 之间到底发生了什么,写一个包装器:LogSer<S> 自己实现 Serializer,每个方法先打印,再转给里面的 S。难点是递归:serialize_field("tags", &self.tags) 里的 Vec 会对 serde_json 的 Serializer 调 serialize_seq,绕过包装器。解决办法是把每个往下传的值包一层 Val,它的 Serialize 实现把内层的 serializer 再包成 LogSer:
/// `Val(&x).serialize(s)` serializes `x` into `LogSer(s)`: this is how the
/// wrapper follows the recursion into nested values.
pub struct Val<'a, T: ?Sized>(pub &'a T);
impl<T: ?Sized + Serialize> Serialize for Val<'_, T> {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
self.0.serialize(LogSer(s))
}
}
pub struct LogSer<S>(pub S);
pub struct LogCompound<C>(C);
impl<S: Serializer> Serializer for LogSer<S> {
type Ok = S::Ok;
type Error = S::Error;
type SerializeSeq = LogCompound<S::SerializeSeq>;
// ... 其余六个复合类型同样包一层 LogCompound
type SerializeStruct = LogCompound<S::SerializeStruct>;
// serialize_bool ... serialize_bytes 由一个宏生成:打印方法名和参数,再转发
fn serialize_newtype_variant<T: ?Sized + Serialize>(
self,
name: &'static str,
index: u32,
variant: &'static str,
value: &T,
) -> Result<S::Ok, S::Error> {
log(format!("serialize_newtype_variant({name:?}, {index}, {variant:?})"));
with_depth(|| self.0.serialize_newtype_variant(name, index, variant, &Val(value)))
}
fn serialize_struct(self, name: &'static str, len: usize) -> Result<Self::SerializeStruct, S::Error> {
log(format!("serialize_struct({name:?}, {len})"));
let inner = with_depth(|| self.0.serialize_struct(name, len))?;
DEPTH.with(|d| d.set(d.get() + 1));
Ok(LogCompound(inner))
}
// ... 其余方法同一模式
}
impl<C: ser::SerializeStruct> ser::SerializeStruct for LogCompound<C> {
type Ok = C::Ok;
type Error = C::Error;
fn serialize_field<T: ?Sized + Serialize>(&mut self, key: &'static str, value: &T) -> Result<(), C::Error> {
log(format!("serialize_field({key:?})"));
with_depth(|| self.0.serialize_field(key, &Val(value)))
}
fn end(self) -> Result<C::Ok, C::Error> {
end_compound(|| self.0.end())
}
}
log 按一个线程局部的深度缩进打印,with_depth 在调用期间把深度加一,end_compound 先减一再打印 end。另外给 serde_json 一个 LogWriter 当输出,它的 io::Write::write 把每次写入的字节打印成 write "...",这样能看到每次调用写了什么。src/bin/log_ser.rs:
use exp_serde_overview::log_ser::{LogWriter, Val};
use exp_serde_overview::model::{Post, Status};
use serde::Serialize;
fn main() {
let post = Post {
title: "serde".to_string(),
tags: vec!["rust".to_string(), "json".to_string()],
status: Status::Published(2026),
};
let mut ser = serde_json::Serializer::new(LogWriter(Vec::new()));
Val(&post).serialize(&mut ser).unwrap();
let out = ser.into_inner().0;
println!("output: {}", String::from_utf8(out).unwrap());
}
输出(M2 Max、macOS、rustc 1.99.0,跑了 3 次,完全相同):
serialize_struct("Post", 3)
write "{"
serialize_field("title")
write "\""
write "title"
write "\""
write ":"
serialize_str("serde")
write "\""
write "serde"
write "\""
serialize_field("tags")
write ","
write "\""
write "tags"
write "\""
write ":"
serialize_seq(Some(2))
write "["
serialize_element
serialize_str("rust")
write "\""
write "rust"
write "\""
serialize_element
write ","
serialize_str("json")
write "\""
write "json"
write "\""
end
write "]"
serialize_field("status")
write ","
write "\""
write "status"
write "\""
write ":"
serialize_newtype_variant("Status", 1, "Published")
write "{"
write "\""
write "Published"
write "\""
write ":"
serialize_u32(2026)
write "2026"
write "}"
end
write "}"
output: {"title":"serde","tags":["rust","json"],"status":{"Published":2026}}
逐步对照
1. serialize_struct("Post", 3) 写 {。 serde_json 的 serialize_struct 不关心名字 "Post",只在开了 arbitrary_precision / raw_value 时认两个魔法名字,其余一律转给 serialize_map(Some(len))。serialize_map 调 formatter.begin_object 写 {,返回 Compound::Map { ser, state: State::First }。len 只用来判断是不是 0:是 0 就当场写 },状态记成 State::Empty。Compound 就是那个状态对象,七个复合类型的关联类型在 serde_json 里都是它。
2. serialize_field("title") 写 "title":"serde"。 SerializeStruct::serialize_field 对 Compound::Map 直接转给 SerializeMap::serialize_entry,而 serde 里 serialize_entry 的默认实现是先 serialize_key 再 serialize_value。serde_json 的 serialize_key 先调 begin_object_key(writer, first),CompactFormatter 用的是 Formatter 的默认实现:第一个键什么也不写,之后的键前面写 ,;然后把状态改成 State::Rest,用 MapKeySerializer 写键。键 "title" 是直接交给 serde_json 内部的,所以日志里没有对应的 serialize_str,只有三次 write:format_escaped_str 分别写开引号、内容、闭引号。serialize_value 写 :,再 value.serialize(&mut **ser):String 的 Serialize 就是 serializer.serialize_str(self),日志里的 serialize_str("serde") 是值这一侧经过包装器的那次调用。
3. serialize_field("tags") 写 ,"tags":[...]。 这次 first 是 false,先写 ,。Vec<T> 的 Serialize 由 seq_impl! 生成,函数体是 serializer.collect_seq(self);collect_seq 是 Serializer 的默认方法,用迭代器的长度提示调 serialize_seq(Some(2)),逐个 serialize_element,最后 end。serde_json 的 serialize_seq 写 [,serialize_element 在第二个元素前写 ,,end 写 ]。日志里第二个 serialize_element 下面先出现 write "," 再出现 serialize_str("json"),就是这个顺序。
4. serialize_field("status") 写 ,"status":{"Published":2026}。 Status 的 Serialize 调 serialize_newtype_variant,serde_json 把它写成只有一个键的对象:{、变体名、:、值、}。u32 写成 2026 只有一次 write,数字不需要转义。
5. end 写 }。 SerializeStruct::end 转给 SerializeMap::end,状态不是 Empty 就写 }。
整个过程里没有任何中间结构:derive 生成的代码每调一次方法,serde_json 当场就把对应的字节写进 Vec。逗号、冒号、缩进全由 Formatter 决定,换成 to_string_pretty 只是换成 PrettyFormatter,Post 的代码一行不变(serde_json 的 Serializer)。
生成的 Deserialize
反方向的展开长得多(Post 一个类型就有两百多行)。只摘骨架,细节(__Field 的三种 visit_*、visit_seq、missing_field 的行为)在 生成的 Deserialize:
impl<'de> _serde::Deserialize<'de> for Post {
fn deserialize<__D>(
__deserializer: __D,
) -> _serde::__private229::Result<Self, __D::Error>
where
__D: _serde::Deserializer<'de>,
{
enum __Field {
__field0,
__field1,
__field2,
__ignore,
}
struct __FieldVisitor;
impl<'de> _serde::de::Visitor<'de> for __FieldVisitor {
type Value = __Field;
// ... expecting、visit_u64
fn visit_str<__E>(
self,
__value: &str,
) -> _serde::__private229::Result<Self::Value, __E>
where
__E: _serde::de::Error,
{
match __value {
"title" => _serde::__private229::Ok(__Field::__field0),
"tags" => _serde::__private229::Ok(__Field::__field1),
"status" => _serde::__private229::Ok(__Field::__field2),
_ => _serde::__private229::Ok(__Field::__ignore),
}
}
// ... visit_bytes
}
impl<'de> _serde::Deserialize<'de> for __Field {
#[inline]
fn deserialize<__D>(
__deserializer: __D,
) -> _serde::__private229::Result<Self, __D::Error>
where
__D: _serde::Deserializer<'de>,
{
_serde::Deserializer::deserialize_identifier(
__deserializer,
__FieldVisitor,
)
}
}
struct __Visitor<'de> {
marker: _serde::__private229::PhantomData<Post>,
lifetime: _serde::__private229::PhantomData<&'de ()>,
}
impl<'de> _serde::de::Visitor<'de> for __Visitor<'de> {
type Value = Post;
// ... expecting("struct Post")、visit_seq
#[inline]
fn visit_map<__A>(
self,
mut __map: __A,
) -> _serde::__private229::Result<Self::Value, __A::Error>
where
__A: _serde::de::MapAccess<'de>,
{
let mut __field0: _serde::__private229::Option<String> = _serde::__private229::None;
let mut __field1: _serde::__private229::Option<Vec<String>> = _serde::__private229::None;
let mut __field2: _serde::__private229::Option<Status> = _serde::__private229::None;
while let _serde::__private229::Some(__key) = _serde::de::MapAccess::next_key::<
__Field,
>(&mut __map)? {
match __key {
__Field::__field0 => {
if _serde::__private229::Option::is_some(&__field0) {
return _serde::__private229::Err(
<__A::Error as _serde::de::Error>::duplicate_field("title"),
);
}
__field0 = _serde::__private229::Some(
_serde::de::MapAccess::next_value::<String>(&mut __map)?,
);
}
// ... __field1、__field2 同样
_ => {
let _ = _serde::de::MapAccess::next_value::<
_serde::de::IgnoredAny,
>(&mut __map)?;
}
}
}
let __field0 = match __field0 {
_serde::__private229::Some(__field0) => __field0,
_serde::__private229::None => {
_serde::__private229::de::missing_field("title")?
}
};
// ... __field1、__field2 同样
_serde::__private229::Ok(Post {
title: __field0,
tags: __field1,
status: __field2,
})
}
}
const FIELDS: &'static [&'static str] = &["title", "tags", "status"];
_serde::Deserializer::deserialize_struct(
__deserializer,
"Post",
FIELDS,
__Visitor {
marker: _serde::__private229::PhantomData::<Post>,
lifetime: _serde::__private229::PhantomData,
},
)
}
}
deserialize 函数体里定义了三个局部类型(__Field、__FieldVisitor、__Visitor),真正执行的只有最后一句:把 __Visitor 交给 deserialize_struct。之后读什么、什么时候回调,都由格式决定。模板在 serde_derive 的 de/struct_.rs:visit_map 的循环由 deserialize_map 生成(while let ... next_key::<__Field> 那一行),__Field 和 __FieldVisitor 由 de/identifier.rs 的 deserialize_generated 生成。缺字段时调的 missing_field 在 serde crate 的 private/de.rs 里,是 derive 的运行时,不在 serde_core。
Status 的 Deserialize 结构相同,最后一句是 deserialize_enum(__deserializer, "Status", VARIANTS, __Visitor),__Visitor 实现的是 visit_enum:
match _serde::de::EnumAccess::variant(__data) {
_serde::__private229::Ok((__Field::__field0, __variant)) => {
_serde::de::VariantAccess::unit_variant(__variant)?;
_serde::__private229::Ok(Status::Draft)
}
_serde::__private229::Ok((__Field::__field1, __variant)) => {
_serde::__private229::Result::map(
_serde::de::VariantAccess::newtype_variant::<
u32,
>(__variant),
Status::Published,
)
}
_serde::__private229::Err(__err) => {
_serde::__private229::Err(__err)
}
}
先读出是哪个变体,再按变体的形状要内容:EnumAccess 和 VariantAccess 的分工在 SeqAccess、MapAccess 与 EnumAccess。
反序列化主线:一次 from_str
from_str 把 &str 包成 StrRead,交给 from_trait:
fn from_trait<'de, R, T>(read: R) -> Result<T>
where
R: Read<'de>,
T: de::Deserialize<'de>,
{
let mut de = Deserializer::new(read);
let value = tri!(de::Deserialize::deserialize(&mut de));
// Make sure the whole stream has been consumed.
tri!(de.end());
Ok(value)
}
和序列化一样,实现 serde::Deserializer 的是 &mut Deserializer<R>。Post::deserialize 返回之后还有一步 end:值后面除了空白还有别的字符,就报 trailing characters。
打印每一次调用
反方向的包装器要包四种东西:格式的 Deserializer(类型调它的 deserialize_*)、类型的 Visitor(格式调它的 visit_*)、格式交给 Visitor 的 MapAccess / SeqAccess / EnumAccess / VariantAccess,以及 DeserializeSeed。最后一种是递归的入口:next_key_seed(seed) 里 serde_json 会用一个内部的 Deserializer(键用 MapKey)调 seed.deserialize(..),包一层 LogSeed 才能把那个 Deserializer 也换成 LogDe:
/// Wraps the format's Deserializer: the type calls `deserialize_*` on it.
pub struct LogDe<D>(pub D);
/// Wraps the type's Visitor: the format calls `visit_*` on it.
pub struct LogVis<V>(V);
/// Wraps a DeserializeSeed so that the nested Deserializer is wrapped too.
pub struct LogSeed<T>(T);
pub struct LogAccess<A>(A);
impl<'de, D: Deserializer<'de>> Deserializer<'de> for LogDe<D> {
type Error = D::Error;
// deserialize_any ... deserialize_ignored_any 中只带 visitor 的那些由宏生成
fn deserialize_struct<V: Visitor<'de>>(
self,
name: &'static str,
fields: &'static [&'static str],
visitor: V,
) -> Result<V::Value, D::Error> {
log(format!("deserialize_struct({name:?}, {fields:?})"));
indented(|| self.0.deserialize_struct(name, fields, LogVis(visitor)))
}
// ...
}
impl<'de, V: Visitor<'de>> Visitor<'de> for LogVis<V> {
type Value = V::Value;
// expecting 直接转发;visit_bool ... visit_byte_buf 由宏生成
fn visit_map<A: de::MapAccess<'de>>(self, map: A) -> Result<V::Value, A::Error> {
log("visit_map");
indented(|| self.0.visit_map(LogAccess(map)))
}
// ...
}
impl<'de, T: DeserializeSeed<'de>> DeserializeSeed<'de> for LogSeed<T> {
type Value = T::Value;
fn deserialize<D: Deserializer<'de>>(self, d: D) -> Result<T::Value, D::Error> {
self.0.deserialize(LogDe(d))
}
}
impl<'de, A: de::MapAccess<'de>> de::MapAccess<'de> for LogAccess<A> {
type Error = A::Error;
fn next_key_seed<K: DeserializeSeed<'de>>(&mut self, seed: K) -> Result<Option<K::Value>, A::Error> {
log("next_key_seed");
let r = indented(|| self.0.next_key_seed(LogSeed(seed)));
if let Ok(None) = r {
log(" => None");
}
r
}
fn next_value_seed<T: DeserializeSeed<'de>>(&mut self, seed: T) -> Result<T::Value, A::Error> {
log("next_value_seed");
indented(|| self.0.next_value_seed(LogSeed(seed)))
}
// ... size_hint 直接转发
}
// SeqAccess、EnumAccess、VariantAccess 同一模式
包装器的每个 visit_* 都原样转给内层的同名方法,所以日志记录的是格式调了哪个方法;内层 Visitor 收到之后再转给哪个默认方法,不在日志里。src/bin/log_de.rs:
use exp_serde_overview::log_de::LogDe;
use exp_serde_overview::model::Post;
use serde::Deserialize;
fn main() {
let json = r#"{"title":"serde","tags":["rust","json"],"status":{"Published":2026}}"#;
let mut de = serde_json::Deserializer::from_str(json);
let post = Post::deserialize(LogDe(&mut de)).unwrap();
de.end().unwrap();
println!("{post:?}");
}
输出(M2 Max、macOS、rustc 1.99.0,跑了 3 次,完全相同):
deserialize_struct("Post", ["title", "tags", "status"])
visit_map
next_key_seed
deserialize_identifier
visit_borrowed_str("title")
next_value_seed
deserialize_string
visit_borrowed_str("serde")
next_key_seed
deserialize_identifier
visit_borrowed_str("tags")
next_value_seed
deserialize_seq
visit_seq
next_element_seed
deserialize_string
visit_borrowed_str("rust")
next_element_seed
deserialize_string
visit_borrowed_str("json")
next_element_seed
=> None
next_key_seed
deserialize_identifier
visit_borrowed_str("status")
next_value_seed
deserialize_enum("Status", ["Draft", "Published"])
visit_enum
variant_seed
deserialize_identifier
visit_borrowed_str("Published")
newtype_variant_seed
deserialize_u32
visit_u64(2026)
next_key_seed
=> None
Post { title: "serde", tags: ["rust", "json"], status: Published(2026) }
和序列化的日志对比,缩进的方向反过来了:序列化时最外层是类型调格式(serialize_struct),往里还是类型调格式;反序列化时类型只调了一次 deserialize_struct,下一层就是格式调类型(visit_map),再下一层类型又调格式(next_key_seed)。控制权在两边之间来回交了好几次。
逐步对照
1. deserialize_struct → visit_map。 serde_json 的 deserialize_struct 跳过空白看下一个字节:{ 就吃掉它,调 visitor.visit_map(MapAccess::new(self));[ 就调 visit_seq;别的字节报 invalid type。visit_map 返回后再调 end_map 吃掉 }。name 和 fields 两个参数 serde_json 都没用(参数名是 _name、_fields)。非自描述格式要用它们:bincode 1.3.3 的 deserialize_struct 就是 self.deserialize_tuple(fields.len(), visitor),按字段数当成元组读。
2. next_key_seed → deserialize_identifier → visit_borrowed_str("title")。 生成代码调的是 next_key::<__Field>(),它是 MapAccess 的默认方法,转给 next_key_seed(PhantomData);PhantomData<T> 作为 seed 时就是 T::deserialize(deserializer)。serde_json 的 next_key_seed 先判断还有没有键:看到 } 返回 None;第一个键必须以 " 开头,之后的键前面必须有 ,;各种不合法的情况分别报 KeyMustBeAString、TrailingComma、ExpectedObjectCommaOrEnd。有键就用 MapKey 这个专门读键的 Deserializer 调 seed。__Field::deserialize 调 deserialize_identifier,MapKey 把它连同 str、string 等一起用 forward_to_deserialize_any! 转给自己的 deserialize_any:读出字符串,没有转义时是 Reference::Borrowed,调 visit_borrowed_str。__FieldVisitor 没实现 visit_borrowed_str,默认实现转给 visit_str,match 出 __Field::__field0。
3. next_value_seed → deserialize_string → visit_borrowed_str("serde")。 next_value_seed 先 parse_object_colon 吃掉 :,再用主 Deserializer 调 seed。String 的 Deserialize 调 deserialize_string(StringVisitor);serde_json 的 deserialize_string 直接转给 deserialize_str,按能不能借用调 visit_borrowed_str 或 visit_str。StringVisitor 拿到借用的 &str 后 to_owned() 一次。
问: title 是 String,日志里 serde_json 调的却是 visit_borrowed_str,难道 String 借用了输入?
答: 没有。调哪个 visit_* 是格式根据输入决定的:from_str 的输入是 &'de str,字符串里没有转义时,serde_json 可以直接切出一段 &'de str,于是它调 visit_borrowed_str,意思是「这段可以借,借不借由你」。String 的 visitor 不借,它只实现了 visit_str(和 visit_string),visit_borrowed_str 的默认实现转给 visit_str,在那里 to_owned()。把字段改成 &'de str 时,visitor 会实现 visit_borrowed_str 并直接收下这段借用;输入里有转义(serde_json 只能把解码后的内容放进内部的 scratch 缓冲,调 visit_str)时,同一个字段就会报错。这些在 生命周期 里讲。
4. tags:deserialize_seq → visit_seq → 三次 next_element_seed。 Vec<T> 的 Deserialize 调 deserialize_seq,它的 visitor 在 visit_seq 里 while let Some(value) = seq.next_element()? 一直取到 None。serde_json 的 deserialize_seq 看到 [ 才调 visit_seq。两个元素,三次 next_element_seed:最后一次读到 ],返回 None。
5. status:deserialize_enum → visit_enum → variant_seed → newtype_variant_seed。 serde_json 的 deserialize_enum 看到 { 时调 visit_enum(VariantAccess::new(self)),看到 " 时调 visit_enum(UnitVariantAccess::new(self))("Draft" 走这条)。variant_seed 也用 MapKey 读变体名,再吃掉 :;newtype_variant_seed 就是 seed.deserialize(self.de)。u32 调 deserialize_u32,serde_json 把 i8 到 u64 的整数提示都转给 deserialize_number,放得进 u64 的非负整数一律回调 visit_u64。u32 的 visitor 用 uint_to_self!(u64:visit_u64) 实现了 visit_u64:u32::try_from(v),放不下就报 invalid value。这就是上一页说的「提示和回调不必一一对应」。
6. 最后一次 next_key_seed 返回 None。 看到 },while let 结束。三个 Option 都是 Some,不走 missing_field,构造出 Post。回到 deserialize_struct,end_map 吃掉 };回到 from_trait,end 确认后面没有别的字符。
Visitor 收到几次 visit_* 调用?平台 M2 Max、macOS、rustc 1.99.0。还是同一个 Post 和同一个 LogDe 包装器,换一段输入:字段顺序打乱,多一个 Post 没有的字段 extra,它的值是一个嵌套的对象;status 用不带数据的变体。
use exp_serde_overview::log_de::LogDe;
use exp_serde_overview::model::Post;
use serde::Deserialize;
fn main() {
// Fields out of order, one unknown key, a unit variant.
let json = r#"{"tags":[],"extra":{"a":[1,2]},"title":"x","status":"Draft"}"#;
let mut de = serde_json::Deserializer::from_str(json);
let post = Post::deserialize(LogDe(&mut de)).unwrap();
de.end().unwrap();
println!("{post:?}");
}
extra 的值 {"a":[1,2]} 里有一个对象、一个键、一个数组、两个数。next_value_seed 下面会出现几行 visit_*?
动脑筋答案
deserialize_struct("Post", ["title", "tags", "status"])
visit_map
next_key_seed
deserialize_identifier
visit_borrowed_str("tags")
next_value_seed
deserialize_seq
visit_seq
next_element_seed
=> None
next_key_seed
deserialize_identifier
visit_borrowed_str("extra")
next_value_seed
deserialize_ignored_any
visit_unit
next_key_seed
deserialize_identifier
visit_borrowed_str("title")
next_value_seed
deserialize_string
visit_borrowed_str("x")
next_key_seed
deserialize_identifier
visit_borrowed_str("status")
next_value_seed
deserialize_enum("Status", ["Draft", "Published"])
visit_enum
variant_seed
deserialize_identifier
visit_borrowed_str("Draft")
unit_variant
next_key_seed
=> None
Post { title: "x", tags: [], status: Draft }
答案是 1 次:visit_unit。
"extra"经__FieldVisitor::visit_str匹配到_ => __Field::__ignore,生成代码对它调next_value::<IgnoredAny>()。IgnoredAny的Deserialize调deserialize_ignored_any。- serde_json 的
deserialize_ignored_any调ignore_value在字节层面跳过整个值(括号配对、字符串、数字都只扫过去,不构造任何东西),然后只调一次visitor.visit_unit()。对象、键、数组、两个数,一个都没有交给Visitor。换一个不会「跳过」的格式,IgnoredAny的 visitor 也能逐个接收再丢掉,但 serde_json 选择了直接跳过。 - 字段顺序打乱没有影响:
visit_map的while let按 JSON 里的顺序取键,match到哪个就填哪个Option。tags是空数组,next_element_seed只调一次就返回None。 "Draft"以"开头,deserialize_enum走UnitVariantAccess,variant_seed读出变体名,unit_variant什么也不用读。
小结
- derive 生成的
Serialize是三步:serialize_struct(name, len)拿状态对象,每个字段serialize_field(键, &值),最后end;len展开成false as usize + 1 + 1 + 1,起点tag_field_exists as usize来自 1.0.87 的 #1474。enum 是一个match,每个变体交出类型名、序号和变体名。 - serde_json 的
serialize_struct转给serialize_map,写{;serialize_field经serialize_entry拆成serialize_key(,、键)和serialize_value(:、值);end写}。日志证明每次调用当场写出字节,没有中间结构。 - derive 生成的
Deserialize定义__Field、__FieldVisitor、__Visitor三个局部类型,只执行一句deserialize_struct(.., __Visitor);之后由 serde_json 回调visit_map,visit_map再循环调next_key/next_value,控制权在格式和类型之间来回交接。 - 键经
deserialize_identifier由MapKey转给deserialize_any读出,visit_borrowed_str默认转visit_str后匹配成__Field;u32字段收到的是visit_u64(2026),由u32的 visitor 检查范围。 - 未知字段走
IgnoredAny,serde_json 在字节层面跳过整个值,只回调一次visit_unit。