反序列化时字段为何被多次访问?Serde_test测试问题排查
问题:serde中visit_map被调用两次的原因?
我在运行serde_test,测试官方文档里的示例代码,只自行编写了fn main()函数。我的目标是验证visit_map的调用符合预期——每个结构体仅被调用一次,但测试发现每个字段被调用了两次,这是为什么?
目前没有文档说明visit_map会被调用两次,作为用户有理由认为只会调用一次。如果要在visit_map中执行复杂操作,手动实现Deserialize的成本会很高。
测试代码
use std::fmt; use serde::de::{self, Deserialize, Deserializer, MapAccess, SeqAccess, Visitor}; #[derive(Debug, PartialEq)] struct Duration { secs: u64, nanos: u32, } impl<'de> Deserialize<'de> for Duration { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de>, { #[derive(Debug)] enum Field { Secs, Nanos, } // This part could also be generated independently by: // // #[derive(Deserialize)] // #[serde(field_identifier, rename_all = "lowercase")] // enum Field { Secs, Nanos } impl<'de> Deserialize<'de> for Field { fn deserialize<D>(deserializer: D) -> Result<Field, D::Error> where D: Deserializer<'de>, { struct FieldVisitor; impl<'de> Visitor<'de> for FieldVisitor { type Value = Field; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("`secs` or `nanos`") } fn visit_str<E>(self, value: &str) -> Result<Field, E> where E: de::Error, { match value { "secs" => Ok(Field::Secs), "nanos" => Ok(Field::Nanos), _ => Err(de::Error::unknown_field(value, FIELDS)), } } } deserializer.deserialize_identifier(FieldVisitor) } } struct DurationVisitor; impl<'de> Visitor<'de> for DurationVisitor { type Value = Duration; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { formatter.write_str("struct Duration") } fn visit_seq<V>(self, mut seq: V) -> Result<Duration, V::Error> where V: SeqAccess<'de>, { let secs = seq .next_element()? .ok_or_else(|| de::Error::invalid_length(0, &self))?; let nanos = seq .next_element()? .ok_or_else(|| de::Error::invalid_length(1, &self))?; Ok(Duration { secs, nanos }) } fn visit_map<V>(self, mut map: V) -> Result<Duration, V::Error> where V: MapAccess<'de>, { let mut secs = None; let mut nanos = None; while let Some(key) = map.next_key()? { println!(" Found Key {:?}", key); match key { Field::Secs => { if secs.is_some() { return Err(de::Error::duplicate_field("secs")); } secs = Some(map.next_value()?); } Field::Nanos => { if nanos.is_some() { return Err(de::Error::duplicate_field("nanos")); } nanos = Some(map.next_value()?); } } } let secs = secs.ok_or_else(|| de::Error::missing_field("secs"))?; let nanos = nanos.ok_or_else(|| de::Error::missing_field("nanos"))?; Ok(Duration { secs, nanos }) } } const FIELDS: &'static [&'static str] = &["secs", "nanos"]; deserializer.deserialize_struct("Duration", FIELDS, DurationVisitor) } } fn main() { use serde_test::Token; let obj = Duration { secs: 10, nanos: 12, }; serde_test::assert_de_tokens( &obj, &[ Token::Struct { name: "Duration", len: 3, }, Token::Str("secs"), Token::U64(10), Token::Str("nanos"), Token::U64(12), // the following fields can be omitted, possibly because of visit_str is a // function that would be called on demand, that is, as long as some Token // is being met. That leads to a unusual result: incomplete group of // matched tokens is acceptable! Token::StructEnd, ], ); }
运行输出
$ cargo run Found Key Secs Found Key Nanos Found Key Secs Found Key Nanos
原因分析及解决办法
原因
serde_test的assert_de_tokens默认会执行两次反序列化:
- 第一次是正常反序列化,验证结果是否与预期对象一致;
- 第二次是兼容性校验,验证反序列化逻辑是否能正确处理包含未知字段的输入(即使测试用例里没有未知字段,这个校验步骤依然会触发)。
这两次反序列化都会调用visit_map,所以会看到每个字段的输出重复出现。
解决办法
如果只需要单次反序列化验证,直接用assert_de_tokens_single替代assert_de_tokens即可。同时注意测试用例中Token::Struct的len参数写了3,但实际只有2个字段,建议改成正确的2:
修改后的main函数:
fn main() { use serde_test::Token; let obj = Duration { secs: 10, nanos: 12, }; serde_test::assert_de_tokens_single( &obj, &[ Token::Struct { name: "Duration", len: 2, }, Token::Str("secs"), Token::U64(10), Token::Str("nanos"), Token::U64(12), Token::StructEnd, ], ); }
运行修改后的代码,就只会看到一次字段输出了。
内容的提问来源于stack exchange,提问作者absuu
相关产品推荐
相关产品推荐

