Rust Serde:依赖其他字段的deserialize_with触发`missing field`错误怎么办?
Serde反序列化时生成派生字段的解决方案
问题背景
尝试通过Serde反序列化JSON生成Person结构体,希望full_name字段由first_name和last_name拼接生成,但JSON中不包含该字段,当前代码运行报错:
Error("missing field full_name", line: 5, column: 9)
问题代码:
#[derive(Debug, Deserialize)] struct Person { first_name: String, last_name: String, #[serde(deserialize_with = "full_name_deserializer")] full_name: String, } fn full_name_deserializer<'de, D>(deserializer: D) -> Result<String, D::Error> where D: Deserializer<'de>, { let person: Person = Deserialize::deserialize(deserializer)?; Ok(format!("{} - {}", person.first_name, person.last_name)) } let data = r#" { "first_name": "John", "last_name": "Doe" }"#; match serde_json::from_str::<Person>(data) { Ok(p) => println!("Me: {:?}",p), Err(e) => println!("Error: {:?}",e) }
补充说明:考虑给full_name添加skip_deserializing属性,反序列化后再生成该字段,但不确定是否有内置钩子触发此操作;曾关注到Serde有添加finalizer属性钩子的提案,但该功能尚未实现。
可行解决方案
方案一:反序列化后手动赋值(最简洁)
给full_name添加#[serde(skip_deserializing)]跳过反序列化,完成后手动拼接生成字段:
use serde::Deserialize; #[derive(Debug, Deserialize)] struct Person { first_name: String, last_name: String, #[serde(skip_deserializing)] full_name: String, } fn main() { let data = r#" { "first_name": "John", "last_name": "Doe" }"#; let mut person = serde_json::from_str::<Person>(data).unwrap(); person.full_name = format!("{} - {}", person.first_name, person.last_name); println!("Me: {:?}", person); }
方案二:手动实现Deserialize trait
不依赖自动派生,手动编写反序列化逻辑,在过程中直接生成full_name:
use serde::{Deserialize, Deserializer}; use serde_json::Value; #[derive(Debug)] struct Person { first_name: String, last_name: String, full_name: String, } impl<'de> Deserialize<'de> for Person { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de>, { let value = Value::deserialize(deserializer)?; let first_name = value["first_name"] .as_str() .ok_or_else(|| serde::de::Error::missing_field("first_name"))? .to_string(); let last_name = value["last_name"] .as_str() .ok_or_else(|| serde::de::Error::missing_field("last_name"))? .to_string(); let full_name = format!("{} - {}", first_name, last_name); Ok(Person { first_name, last_name, full_name, }) } } fn main() { let data = r#" { "first_name": "John", "last_name": "Doe" }"#; match serde_json::from_str::<Person>(data) { Ok(p) => println!("Me: {:?}", p), Err(e) => println!("Error: {:?}", e), } }
方案三:使用中间结构体过渡
先反序列化到不含full_name的中间结构体,再通过转换生成目标结构体:
use serde::Deserialize; #[derive(Debug, Deserialize)] struct PersonRaw { first_name: String, last_name: String, } #[derive(Debug)] struct Person { first_name: String, last_name: String, full_name: String, } impl From<PersonRaw> for Person { fn from(raw: PersonRaw) -> Self { Person { first_name: raw.first_name, last_name: raw.last_name, full_name: format!("{} - {}", raw.first_name, raw.last_name), } } } fn main() { let data = r#" { "first_name": "John", "last_name": "Doe" }"#; let raw_person = serde_json::from_str::<PersonRaw>(data).unwrap(); let person = Person::from(raw_person); println!("Me: {:?}", person); }
关于Serde的finalizer钩子
你提到的添加finalizer属性钩子的提案确实尚未合并到Serde主分支,目前无法通过内置钩子实现反序列化后的自动字段生成,上述三种方案是当前可行的解决方式。
内容的提问来源于stack exchange,提问作者sudoExclamationExclamation
相关产品推荐
相关产品推荐

