You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 04:15:30