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

Rust中如何将name字段反序列化为firstname和lastname字段?

将JSON单个字段反序列化为结构体两个字段的解决方案

问题背景

需要把JSON中的name字段拆分后赋值给Rust结构体User的firstname和lastname字段,当前写法触发错误:missing field 'name',相关代码如下:

结构体定义

struct User {
    #[serde(rename = "name", deserialize_with = "deserialize_firstname")]
    firstname: String,

    #[serde(deserialize_with = "deserialize_lastname")]
    #[serde(rename = "name")]
    lastname: String,
}

反序列化函数(示例)

fn deserialize_firstname<'de, D>(deserializer: D) -> Result<String, D::Error>
where
    D: serde::Deserializer<'de>,
{
    let s: &str = serde::Deserialize::deserialize(deserializer)?;
    let re = Regex::new(r"^\W*([\w-]+)").unwrap();
    let m = re.find(s);

    match m {
        Some(value) => Ok(value.as_str().to_string()),
        None => Ok("".to_string()),
    }
}

错误信息

err: DeserializeError { field: None, kind: Message("missing field `name`") }

错误原因

两个结构体字段都通过rename = "name"绑定到JSON的同一个字段,serde会尝试从JSON中读取两次name值,但JSON里仅存在一个name字段,第二次读取时就会触发缺失字段错误。

解决方法

方法一:临时结构体中转(推荐,无额外依赖)

先定义一个只包含name字段的临时结构体,完成反序列化后再拆分字符串到目标User结构体的两个字段:

use serde::Deserialize;
use regex::Regex;

// 临时结构体,用于接收JSON中的name字段
#[derive(Deserialize)]
struct TempUser {
    name: String,
}

// 目标结构体
#[derive(Debug)]
struct User {
    firstname: String,
    lastname: String,
}

// 为User实现自定义反序列化逻辑
impl<'de> Deserialize<'de> for User {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        // 先反序列化到临时结构体
        let temp_user = TempUser::deserialize(deserializer)?;
        
        // 提取firstname(复用你的正则逻辑)
        let first_re = Regex::new(r"^\W*([\w-]+)").unwrap();
        let firstname = first_re.find(&temp_user.name)
            .map(|m| m.as_str().to_string())
            .unwrap_or_default();
        
        // 提取lastname(这里示例取末尾的单词,根据你的实际需求调整正则)
        let last_re = Regex::new(r"([\w-]+)\W*$").unwrap();
        let lastname = last_re.find(&temp_user.name)
            .map(|m| m.as_str().to_string())
            .unwrap_or_default();
        
        Ok(User { firstname, lastname })
    }
}

方法二:使用serde_with库简化实现

如果可以引入额外依赖,serde_with库提供的工具能简化自定义反序列化流程:

  1. 添加依赖到Cargo.toml:
[dependencies]
serde_with = "3.0"
  1. 实现自定义反序列化转换:
use serde::Deserialize;
use serde_with::DeserializeAs;
use regex::Regex;

#[derive(Deserialize, Debug)]
struct User {
    #[serde(deserialize_as = "NamePart<First>")]
    firstname: String,
    #[serde(deserialize_as = "NamePart<Last>")]
    lastname: String,
}

// 标记枚举,区分要提取的是名还是姓
enum First {}
enum Last {}

// 通用的姓名拆分转换器
struct NamePart<T>;

impl<'de, T> DeserializeAs<'de, String> for NamePart<T>
where
    T: NameMarker,
{
    fn deserialize_as<D>(deserializer: D) -> Result<String, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let full_name: String = String::deserialize(deserializer)?;
        
        if std::any::TypeId::of::<T>() == std::any::TypeId::of::<First>() {
            // 提取firstname的逻辑
            let re = Regex::new(r"^\W*([\w-]+)").unwrap();
            Ok(re.find(&full_name).map(|m| m.as_str().to_string()).unwrap_or_default())
        } else {
            // 提取lastname的逻辑
            let re = Regex::new(r"([\w-]+)\W*$").unwrap();
            Ok(re.find(&full_name).map(|m| m.as_str().to_string()).unwrap_or_default())
        }
    }
}

// 定义标记 trait
trait NameMarker {}
impl NameMarker for First {}
impl NameMarker for Last {}

总结

最直接可靠的方案是使用临时结构体中转,无需额外依赖,逻辑清晰易懂。核心思路是先一次性读取JSON中的name字段,再在代码中拆分字符串赋值到结构体的两个字段,避免让serde重复读取同一个JSON字段。

内容的提问来源于stack exchange,提问作者Antonin GAVREL

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 23:54:50