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库提供的工具能简化自定义反序列化流程:
- 添加依赖到
Cargo.toml:
[dependencies] serde_with = "3.0"
- 实现自定义反序列化转换:
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
相关产品推荐
相关产品推荐

