value 模块与 IntoDeserializer
主线位置:反序列化主线上的「迷你格式」。主线里格式是 serde_json,输入是一段 JSON 文本;这一页的格式是一个已经在内存里的 Rust 值(一个
&str、一个u32、一个Vec、一串键值对)。de/value.rs为每种值写了一个最小的Deserializer:它把值交给Visitor,别的什么也不做。
flowchart TB s["&str On"] --> into[into_deserializer] into --> sd[StrDeserializer] sd --> de["Setting::deserialize"] de -->|deserialize_enum| ve["visit_enum(self)"] ve --> vs[variant_seed] vs -->|visit_str| field["__Field On"] vs --> uo[UnitOnly] uo --> unit[unit_variant] unit --> done["Ok(Setting::On)"]
一个值就是一份输入
什么时候需要「把一个 Rust 值当输入再反序列化一次」?手上的数据已经拆好了,但想复用某个类型的 Deserialize:命令行参数里的 "On" 要变成一个 enum,环境变量的键值对要变成一个配置结构体,一个格式自己的 MapAccess 里要把某个值交给下一层。这些场合不需要解析,只需要一个能把值递给 Visitor 的 Deserializer。
整数、bool、char、浮点的那一批由宏 primitive_deserializer! 生成,核心只有这几行:
impl<'de, E> de::Deserializer<'de> for $name<E>
where
E: de::Error,
{
type Error = E;
forward_to_deserialize_any! {
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str
string bytes byte_buf option unit unit_struct newtype_struct seq
tuple tuple_struct map struct enum identifier ignored_any
}
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
visitor.$method(self.value $($cast)*)
}
}
forward_to_deserialize_any! 把所有 deserialize_* 都转给 deserialize_any(这个宏见 Deserialize、Deserializer 与 Visitor),deserialize_any 调一次和值类型对应的 visit_*。也就是说,这些 Deserializer 不看类型提示:目标类型要的是 u16,U32Deserializer 照样调 visit_u32,转换由目标类型的 Visitor 负责,规则和标准库类型的实现里一样,越界报 invalid value。
整个模块的类型:
| 类型 | 从什么来 | 调什么 |
|---|---|---|
BoolDeserializer、I8Deserializer … F64Deserializer、CharDeserializer | 对应的基本类型 | 同名 visit_*(isize / usize 转成 visit_i64 / visit_u64) |
U32Deserializer | u32 | visit_u32;另外实现了 deserialize_enum,按下标选 unit 变体 |
StrDeserializer | &'a str | visit_str;deserialize_enum 按名字选 unit 变体 |
BorrowedStrDeserializer | &'de str(只能 new) | visit_borrowed_str;同上 |
StringDeserializer、CowStrDeserializer | String、Cow<str> | visit_string(Cow::Borrowed 调 visit_str);同上 |
BytesDeserializer、BorrowedBytesDeserializer | &[u8] | visit_bytes / visit_borrowed_bytes |
UnitDeserializer、NeverDeserializer | ()、! | visit_unit(deserialize_option 调 visit_none);后者不可能被构造 |
SeqDeserializer | 任意迭代器;Vec、BTreeSet、HashSet | visit_seq,元素逐个 into_deserializer() |
MapDeserializer | 产出 (K, V) 的迭代器;BTreeMap、HashMap | visit_map;deserialize_seq 时把每对当成二元组 |
SeqAccessDeserializer、MapAccessDeserializer、EnumAccessDeserializer | 一个现成的 SeqAccess / MapAccess / EnumAccess | visit_seq / visit_map / visit_enum |
最后一行是适配器:它们不持有值,持有的是另一个格式交来的访问器,作用是把「已经进了 visit_map 的半路」重新包装成一个 Deserializer,交给另一个类型的 Deserialize。后面有例子。
IntoDeserializer:值怎么变成 Deserializer
IntoDeserializer 在 de/mod.rs 里:
pub trait IntoDeserializer<'de, E: Error = value::Error> {
/// The type of the deserializer being converted into.
type Deserializer: Deserializer<'de, Error = E>;
/// Convert this value into a deserializer.
fn into_deserializer(self) -> Self::Deserializer;
}
上表左列的每种值都实现了它,5u8.into_deserializer() 得到 U8Deserializer<E>,vec![1, 2].into_deserializer() 得到 SeqDeserializer<vec::IntoIter<i32>, E>。SeqDeserializer 和 MapDeserializer 对元素的要求也只是「元素实现 IntoDeserializer」:next_element_seed 里取出下一个元素,调 value.into_deserializer(),再交给 seed。所以 Vec<Vec<u8>>、BTreeMap<String, Vec<String>> 都能一层层包下去。
错误类型 E 是参数,默认是 value::Error。做成参数,是因为这些小 Deserializer 经常在另一个格式内部使用,报错要变成那个格式的错误类型。serde_json 的 Value 收到 i128 时就是这样,visit_i128 用 serde 的 I128Deserializer 把它转交给 Number 的 Deserialize,错误类型就是外层传进来的 E:
fn visit_i128<E>(self, value: i128) -> Result<Value, E>
where
E: serde::de::Error,
{
let de = serde::de::value::I128Deserializer::new(value);
Number::deserialize(de).map(Value::Number)
}
单独用的时候没有外层格式,就用默认的 value::Error。实验里因为 E 推不出来,要写成 IntoDeserializer::<value::Error>::into_deserializer(x),或者像下面的 FromStr 那样由返回类型定下来。
每个 XxxDeserializer 自己也实现了 IntoDeserializer,type Deserializer = Self,into_deserializer 原样返回。这让「已经包好的 Deserializer」也能放进 SeqDeserializer / MapDeserializer 当元素,下面的借用例子就靠它。
issue #2498(「Consider implementing IntoDeserializer for serde's IntoDeserializer impls」)给的例子正是 impl IntoDeserializer for BorrowedStrDeserializer { type Deserializer = Self; ... }:没有它,想把一串 BorrowedStrDeserializer 交给 MapDeserializer,得先自己定义一个 struct BorrowedStr<'de>(&'de str) 包一层。PR #2568(「Implement IntoDeserializer for all Deserializers in serde::de::value module」,首次发布于 1.0.214)给模块里的每个 Deserializer 都加了这样一个 impl。PR 说明解释了为什么不干脆写一个对所有 Deserializer 的通用 impl:「blanket implementation IntoDeserializer for Deserializer is impossible right now because this would be a breaking change. External crates may have this such implementation (and serde_json actually have it for Value).」serde_json 的 impl IntoDeserializer for Value 就是这种手写的 type Deserializer = Self。
用法一:从 &str 读 enum
模块文档开头的例子,给一个 derive 了 Deserialize 的 enum 实现 FromStr:
#[derive(Deserialize, Debug)]
enum Setting {
On,
Off,
}
impl FromStr for Setting {
type Err = value::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::deserialize(s.into_deserializer())
}
}
Setting::deserialize 调 deserialize_enum。StrDeserializer 没有把它转给 deserialize_any,而是自己实现了:visitor.visit_enum(self),把自己当成 EnumAccess。variant_seed 用自己(visit_str)去读变体名,返回的 VariantAccess 是 UnitOnly:unit_variant 返回 Ok(()),newtype_variant_seed / tuple_variant / struct_variant 都报 invalid_type(Unexpected::UnitVariant, ..)。一个字符串里只有变体名,没有内容,只能表示 unit 变体。
println!("\"On\".parse() = {:?}", "On".parse::<Setting>());
println!("\"Maybe\".parse() = {:?}", "Maybe".parse::<Setting>());
let e = "Maybe".parse::<Setting>().unwrap_err();
println!("Display: {e}");
// enum from variant index
let by_index = Setting::deserialize(IntoDeserializer::<value::Error>::into_deserializer(1u32));
println!("1u32 -> {by_index:?}");
"On".parse() = Ok(On)
"Maybe".parse() = Err(Error("unknown variant `Maybe`, expected `On` or `Off`"))
Display: unknown variant `Maybe`, expected `On` or `Off`
1u32 -> Ok(Off)
U32Deserializer 也自己实现了 deserialize_enum,读变体标识时调 visit_u32,Visitor 默认转到 visit_u64;derive 生成的变体标识 Visitor 里有 0u64 => __Field::__field0、1u64 => __Field::__field1 这样的分支,所以 1u32 读成第二个变体 Off。其他整数的 Deserializer 没有这个 deserialize_enum,只有 u32:数据模型里变体下标的类型就是 u32(serialize_unit_variant 的 variant_index: u32)。
用法二:MapDeserializer 把键值对当成 map
MapDeserializer::new(iter) 收一个产出 (K, V) 的迭代器,K、V 都实现 IntoDeserializer。它的 MapAccess 分两步:next_key_seed 取出下一对,把值暂存到 self.value,用键的 into_deserializer() 喂 seed;next_value_seed 取出暂存的值:
fn next_value_seed<T>(&mut self, seed: T) -> Result<T::Value, Self::Error>
where
T: de::DeserializeSeed<'de>,
{
let value = self.value.take();
// Panic because this indicates a bug in the program rather than an
// expected failure.
let value = value.expect("MapAccess::next_value called before next_key");
seed.deserialize(value.into_deserializer())
}
先调 next_value 再调 next_key 是 Visitor 写错了,不是输入的问题,所以这里 panic 而不是返回错误。两步协议本身见 SeqAccess、MapAccess 与 EnumAccess。
拿它从一组字符串键值对读一个结构体:
#[derive(Deserialize, Debug)]
struct Config {
host: String,
port: u16,
}
#[derive(Deserialize, Debug)]
struct Names {
host: String,
user: String,
}
// main 里:
let mut env: HashMap<String, String> = HashMap::new();
env.insert("host".into(), "localhost".into());
env.insert("user".into(), "alice".into());
let names = Names::deserialize(MapDeserializer::<_, value::Error>::new(env.clone().into_iter()));
println!("Names <- HashMap<String,String> : {names:?}");
env.remove("user");
env.insert("port".into(), "8080".into());
let cfg = Config::deserialize(MapDeserializer::<_, value::Error>::new(env.into_iter()));
println!("Config <- HashMap<String,String> : {cfg:?}");
// same, but the values are serde_json::Value
let mut m: BTreeMap<String, serde_json::Value> = BTreeMap::new();
m.insert("host".into(), "localhost".into());
m.insert("port".into(), 8080.into());
let cfg = Config::deserialize(MapDeserializer::<_, serde_json::Error>::new(m.into_iter()));
println!("Config <- BTreeMap<String,Value> : {cfg:?}");
Names <- HashMap<String,String> : Ok(Names { host: "localhost", user: "alice" })
Config <- HashMap<String,String> : Err(Error("invalid type: string \"8080\", expected u16"))
Config <- BTreeMap<String,Value> : Ok(Config { host: "localhost", port: 8080 })
全是 String 字段时没问题;port: u16 拿到的是 StringDeserializer,它不看 deserialize_u16 这个提示,照样调 visit_string,u16 的 Visitor 不收字符串。把值换成 serde_json::Value 就行了:Value 实现了 IntoDeserializer<'de, serde_json::Error>,它自己就是一个完整的 Deserializer,数字交出去的是 visit_u64。
问: axum 的 Query<T> 用 serde_urlencoded 读 ?port=8080,port: u16 是可以的。query 里的值也全是字符串,它是怎么做到的?
答: serde_urlencoded 0.7.1 的 Deserializer 内部就是一个 MapDeserializer<'de, PartIterator<'de>, Error>,区别在值的类型:它没有用 StringDeserializer,而是自己写了一个 Part,实现 IntoDeserializer(原样返回自己)和 Deserializer,并且听提示。deserialize_u16 这一类方法由一个宏生成,先按提示解析字符串,再借 serde 的基本类型 Deserializer 交出去(serde_urlencoded-0.7.1/src/de.rs):
macro_rules! forward_parsed_value {
($($ty:ident => $method:ident,)*) => {
$(
fn $method<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where V: de::Visitor<'de>
{
match self.0.parse::<$ty>() {
Ok(val) => val.into_deserializer().$method(visitor),
Err(e) => Err(de::Error::custom(e))
}
}
)*
}
}
"8080".parse::<u16>() 得到 8080u16,into_deserializer() 得到 U16Deserializer,它调 visit_u16。同样的 Config 用 serde_urlencoded::from_str("host=localhost&port=8080") 读出来是 Ok(Config { host: "localhost", port: 8080 })。axum 那边怎么调它,见 Path、Query 与请求元数据和 body 提取器。
MapDeserializer 和 SeqDeserializer 都有一个 end():deserialize_any 在 visit_map / visit_seq 返回之后调它,检查迭代器里还有没有没被读走的元素。SeqDeserializer::end:
pub fn end(self) -> Result<(), E> {
let remaining = self.iter.count();
if remaining == 0 {
Ok(())
} else {
// First argument is the number of elements in the data, second
// argument is the number of elements expected by the Deserialize.
Err(de::Error::invalid_length(
self.count + remaining,
&ExpectedInSeq(self.count),
))
}
}
count 是已经交出去的元素个数。格式自己在 visit_seq 之后查「后面还有没有东西」,和 serde_json 的 end() 查尾部字符是同一种职责:Visitor 只读它要的那么多,多出来的由格式发现。
<(u8, u8)>::deserialize(vec![1u8, 2, 3].into_deserializer()) 报什么?反过来,(u8, u8, u8) 读 vec![1u8, 2] 呢?平台 M2 Max、rustc 1.99.0,错误类型 value::Error:
let t = <(u8, u8)>::deserialize(IntoDeserializer::<value::Error>::into_deserializer(vec![1u8, 2, 3]));
println!("(u8,u8) <- vec![1,2,3] : {t:?}");
let t = <(u8, u8, u8)>::deserialize(IntoDeserializer::<value::Error>::into_deserializer(vec![1u8, 2]));
println!("(u8,u8,u8) <- vec![1,2] : {t:?}");
借用:BorrowedStrDeserializer
StrDeserializer 调的是 visit_str,交出去的字符串只在这次调用里有效,&str 目标借不到它;BorrowedStrDeserializer 调 visit_borrowed_str,字符串的生命周期就是 'de:
&str <- StrDeserializer : Err(Error("invalid type: string \"borrowed?\", expected a borrowed string"))
&str <- BorrowedStrDeserializer : Ok("borrowed?")
两者的区别正是生命周期 'de 与借用里 visit_str 和 visit_borrowed_str 的区别。BorrowedStrDeserializer 没有对应的 IntoDeserializer for &str(那个位置已经给了 StrDeserializer),只能 new。配合 MapDeserializer,可以零拷贝地把一段自定义格式的文本读成 BTreeMap<&str, &str>(src/bin/borrowmap.rs):
use std::collections::BTreeMap;
use serde::Deserialize;
use serde::de::value::{BorrowedStrDeserializer, Error, MapDeserializer};
fn main() {
let input = String::from("host=localhost;user=alice");
let pairs = input.split(';').filter_map(|kv| kv.split_once('=')).map(|(k, v)| {
(BorrowedStrDeserializer::<Error>::new(k), BorrowedStrDeserializer::<Error>::new(v))
});
let map = BTreeMap::<&str, &str>::deserialize(MapDeserializer::new(pairs)).unwrap();
let host = map["host"];
println!("{map:?}");
println!("host points into input: {}", input.as_ptr() as usize == host.as_ptr() as usize - "host=".len());
}
{"host": "localhost", "user": "alice"}
host points into input: true
迭代器的元素是 (BorrowedStrDeserializer, BorrowedStrDeserializer),MapDeserializer 对它们调 into_deserializer(),靠的就是上面冷知识里那个「原样返回自己」的 impl。
&str 的 IntoDeserializer 在 1.0.0 前借用过三天2017-04-14 的提交「Allow borrowing from str's IntoDeserializer」(30e8c84d)把 &'a str 的 impl 改成 IntoDeserializer<'a, E>,StrDeserializer 调 visit_borrowed_str;2017-04-17 的 b8a6e683 把它原样回退了,提交信息只有「This reverts commit 30e8c84d...」,没有写原因,两者都在 1.0.0 之前。回退之后,StrDeserializer<'a, E> 实现的是任意 'de 的 Deserializer<'de>,所以只能调 visit_str。
借用的需求一个月后用新类型补上:PR #940(「add borrowed value deserializers」,首次发布于 1.0.7)加了 BorrowedStrDeserializer 和 BorrowedBytesDeserializer,说明里写「the lifetime of the string is tied to the lifetime of the deserializer. This can be useful when, for example, deserializing into a HashMap<&str, &str> when the keys/values are tied to the deserializer itself.」上面的实验就是这个用例。
用法三:在 visit_map 半路换手
MapAccessDeserializer 解决另一类问题:自己写的 Visitor 已经被格式调进了 visit_map,手里是一个 MapAccess,这时想把剩下的工作交给某个 derive 出来的类型。derive 出来的 Deserialize 要的是一个 Deserializer,不是 MapAccess;MapAccessDeserializer 就是这层包装,deserialize_any 直接 visitor.visit_map(self.map)。
一个字段既可以写成字符串简写,也可以写成完整的结构体:
#[derive(Deserialize, Debug)]
struct Image {
name: String,
#[serde(default = "latest")]
tag: String,
}
fn latest() -> String {
"latest".into()
}
// Accepts "nginx" or {"name": "nginx", "tag": "1.27"}.
fn string_or_struct<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Image, D::Error> {
struct V;
impl<'de> Visitor<'de> for V {
type Value = Image;
fn expecting(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str("an image name or an image struct")
}
fn visit_str<E: de::Error>(self, s: &str) -> Result<Image, E> {
Ok(Image { name: s.to_owned(), tag: latest() })
}
fn visit_map<A: MapAccess<'de>>(self, map: A) -> Result<Image, A::Error> {
// Hand the half-consumed map to the derived impl.
Image::deserialize(MapAccessDeserializer::new(map))
}
}
deserializer.deserialize_any(V)
}
#[derive(Deserialize, Debug)]
struct Service {
#[serde(deserialize_with = "string_or_struct")]
image: Image,
}
{"image":"nginx"} Ok(Service { image: Image { name: "nginx", tag: "latest" } })
{"image":{"name":"nginx","tag":"1.27"}} Ok(Service { image: Image { name: "nginx", tag: "1.27" } })
{"image":{"tag":"1.27"}} Err("missing field `name` at line 1 column 23")
{"image":7} Err("invalid type: integer `7`, expected an image name or an image struct at line 1 column 10")
MapAccessDeserializer 的 Error 是 A::Error,即 serde_json 的错误,所以第三行的 missing field 还带着行列号。数据从头到尾没有缓冲:Image 的 visit_map 直接从 serde_json 的 MapAccess 里读。这和缓冲主线不同,缓冲主线是还不知道该交给谁,只能先读成 Content;这里在 visit_str 和 visit_map 之间做选择的是格式(deserialize_any),进了 visit_map 就已经知道该交给 Image。
serde 自己和 serde_json 在哪里用它
| 位置 | 用了什么 | 做什么 |
|---|---|---|
serde/src/private/de.rs 的 TaggedContentVisitor::visit_seq | SeqAccessDeserializer | 内部标签 enum 的输入是数组时,读完第一个元素(标签),把剩下的 SeqAccess 包起来读成 Content,见内部标签与相邻标签 |
同文件的 IdentifierDeserializer | u64 的 IntoDeserializer、BytesDeserializer、BorrowedBytesDeserializer | #[serde(field_identifier)] enum 最后一个变体是 newtype 时,没匹配上的标识交给它(identifier.rs 生成 IdentifierDeserializer::from(__value))。&str 这一支用的是同文件里一对私有的 StrDeserializer / BorrowedStrDeserializer,它们连 deserialize_enum 也转给 deserialize_any |
同文件 ContentDeserializer 的 IntoDeserializer | 实现 IntoDeserializer | 缓冲主线重放时,Content 树本身就是一个 Deserializer,见 Content |
serde_json Value 的 visit_i128 | I128Deserializer / U128Deserializer | 把 128 位整数转交给 Number |
serde_json EnumDeserializer::variant_seed | String 的 into_deserializer() | Value 当 enum 读时,变体名用 StringDeserializer 交给变体标识的 Visitor |
serde_json BorrowedRawDeserializer | BorrowedStrDeserializer | 借用的 RawValue 经魔法键的值交出原始 JSON 文本 |
serde_json NumberDeserializer(arbitrary_precision) | String 的 into_deserializer() | 任意精度数字以字符串交出 |
derive 生成的普通代码不直接用这个模块:字段、变体标识走格式的 deserialize_identifier,未知字段用 IgnoredAny(见标准库类型的实现)。Content 那一套 ContentRefDeserializer、FlatMapDeserializer 是 serde/src/private/de.rs 里私有的,和这里的公开类型是两回事。serde_json 的魔法键和 RawValue 本身在 Value。
value::Error:只有一句话的错误
#[derive(Clone, PartialEq)]
pub struct Error {
err: ErrorImpl,
}
#[cfg(any(feature = "std", feature = "alloc"))]
type ErrorImpl = Box<str>;
#[cfg(not(any(feature = "std", feature = "alloc")))]
type ErrorImpl = ();
de::Error::custom 把消息 to_string().into_boxed_str() 存起来;invalid_type、unknown_variant 等构造函数都是 de::Error 的默认实现,最后也落到 custom,所以它里面只有拼好的那一句话。没有分配器时连这句话也不存,Display 一律打印 Serde deserialization error。它同时实现了 ser::Error,Debug 打印成 Error("...")(上面实验里看到的就是这个),Display 只打印消息本身。没有位置信息,也没有结构化的错误种类:它是给「没有真正的格式」时用的,比如 FromStr 的 Err。Unexpected 和 Expected 怎么拼出这句话,见错误。
动脑筋答案
(u8,u8) <- vec![1,2,3] : Err(Error("invalid length 3, expected 2 elements in sequence"))
(u8,u8,u8) <- vec![1,2] : Err(Error("invalid length 2, expected a tuple of size 3"))
- 第一个错误来自
SeqDeserializer::end。二元组的Visitor读完两个元素就返回Ok,它并不知道后面还有。deserialize_any接着调end(),迭代器里剩 1 个:invalid_length(self.count + remaining, &ExpectedInSeq(self.count)),即「数据里 3 个、Deserialize要 2 个」,ExpectedInSeq(2)打印成2 elements in sequence。 - 第二个错误来自元组的
Visitor。读第三个元素时next_element返回None,Visitor报invalid_length(2, &self),expected用的是它自己的expecting:a tuple of size 3。end()根本没有机会运行。
同样是「长度不对」,太长由格式(Deserializer)发现,太短由类型(Visitor)发现,所以两句话的后半截不一样。
小结
de::value为基本类型、字符串、字节、()、序列和映射各写了一个最小的Deserializer:所有deserialize_*都转给deserialize_any,调一次和值类型对应的visit_*,不看类型提示。转换由目标类型的Visitor负责。- 字符串类和
U32Deserializer自己实现deserialize_enum,按名字或下标选 unit 变体(UnitOnly),所以"On".into_deserializer()能读成 enum,1u32读成第二个变体。 IntoDeserializer<'de, E = value::Error>把值包成这些Deserializer;E是参数,方便在别的格式里用(serde_json 的Value用I128Deserializer)。每个Deserializer也原样实现了它(1.0.214,PR #2568),可以直接当MapDeserializer的元素。MapDeserializer读HashMap<String, String>时,非字符串字段报invalid type: string "8080", expected u16;值换成serde_json::Value,或像 serde_urlencoded 的Part那样按提示解析,才能读数字。SeqDeserializer/MapDeserializer的end()检查剩余元素:太长由它报expected 2 elements in sequence,太短由Visitor报。BorrowedStrDeserializer(1.0.7,PR #940)调visit_borrowed_str,能零拷贝地读出&'de str;StrDeserializer只能调visit_str。MapAccessDeserializer把半路的MapAccess包成Deserializer,用来在visit_map里把工作交给 derive 出来的类型,不缓冲,错误保留外层格式的位置。value::Error只存一句拼好的消息(Box<str>),给没有真正格式的场合用。