如何基于base.method_name用Serde反序列化区分Rust请求结构体
解决Serde根据嵌套字段
base.method_name区分结构体的问题 Serde默认的#[serde(tag)]属性仅支持顶层字段,无法直接识别嵌套的base.method_name作为区分标签,因此需要通过自定义反序列化逻辑实现需求,具体步骤如下:
1. 定义统一请求枚举
先创建一个枚举来封装两种请求类型,方便后续统一处理:
use serde::{Deserialize, Serialize}; use serde_json::{self, Value}; #[derive(Debug, Deserialize, Serialize)] struct RequestBase { id: i32, method_name: String, } #[derive(Debug, Deserialize, Serialize)] struct LoginRequest { base: RequestBase, email: String, password: String, } #[derive(Debug, Deserialize, Serialize)] struct CreateAccountRequest { base: RequestBase, email: String, password: String, } // 统一处理两种请求的枚举 #[derive(Debug, Serialize)] enum Request { Login(LoginRequest), CreateAccount(CreateAccountRequest), }
2. 为枚举实现自定义反序列化
为Request实现Deserialize trait,核心逻辑是先将JSON解析为通用的Value结构,提取出base.method_name的值后,再定向反序列化为对应的结构体:
impl<'de> Deserialize<'de> for Request { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::Deserializer<'de>, { // 先解析为通用Value,方便提取嵌套字段 let value = Value::deserialize(deserializer)?; // 提取base.method_name的值,若缺失或格式错误则返回错误 let method_name = value .get("base") .and_then(|base| base.get("method_name")) .and_then(|name| name.as_str()) .ok_or_else(|| serde::de::Error::custom("Missing or invalid 'base.method_name' field"))?; // 根据method_name匹配对应的请求结构体 match method_name { "LoginRequest" => { let login_req = LoginRequest::deserialize(value)?; Ok(Request::Login(login_req)) } "CreateAccountRequest" => { let create_req = CreateAccountRequest::deserialize(value)?; Ok(Request::CreateAccount(create_req)) } unknown => Err(serde::de::Error::custom(format!("Unsupported method name: {}", unknown))), } } }
3. 使用示例
现在可以直接将JSON字符串反序列化为Request枚举,通过匹配枚举变体处理不同请求:
fn main() -> Result<(), Box<dyn std::error::Error>> { // 登录请求JSON let login_json = r#"{"base":{"id":1,"method_name":"LoginRequest"},"email":"a@mail.com","password":"xxxx"}"#; let login_req: Request = serde_json::from_str(login_json)?; println!("Parsed login request: {:?}", login_req); // 创建账号请求JSON let create_json = r#"{"base":{"id":1,"method_name":"CreateAccountRequest"},"email":"a@mail.com","password":"xxxx"}"#; let create_req: Request = serde_json::from_str(create_json)?; println!("Parsed create account request: {:?}", create_req); Ok(()) }
扩展说明
- 若后续需要新增其他请求类型,只需在
Request枚举中添加对应变体,并在match分支中补充反序列化逻辑即可。 - 该方法通过通用
Value中转的方式,完美适配嵌套字段作为区分标签的场景,逻辑清晰且易于维护。
内容的提问来源于stack exchange,提问作者eminfedar
相关产品推荐
相关产品推荐

