Rust中如何将带可选参数的扁平JSON反序列化为嵌套结构体与枚举
解决方案
问题原因
你当前的写法中,标记了#[serde(tag = "status")]的Status枚举作为Download的嵌套字段时,serde默认期望status字段对应的值是一个包含status标签和cause(失败场景下)的嵌套对象,但实际你的JSON结构里status、cause都是和id平级的顶级字段,字段匹配逻辑错位导致反序列化失败。
修改方法
仅需给Download结构体的status字段添加#[serde(flatten)]属性即可,修改后的完整代码如下:
use serde::Deserialize; #[derive(Deserialize, Debug)] struct Download { id: i32, #[serde(flatten)] status: Status, } #[derive(Deserialize, Debug)] #[serde(tag = "status")] enum Status { Executing, Successful, Failed { cause: String }, } #[cfg(test)] mod tests { use super::*; #[test] fn status_from_executing() { let s = r#"{"status":"Executing"}"#; serde_json::from_str::<Status>(s).unwrap(); } #[test] fn status_from_succeeded() { let s = r#"{"status":"Successful"}"#; serde_json::from_str::<Status>(s).unwrap(); } #[test] fn status_from_failed() { let s = r#"{"status":"Failed","cause":"Bad thing"}"#; serde_json::from_str::<Status>(s).unwrap(); } #[test] fn download_from_executing() { let s = r#"{"id":1,"status":"Executing"}"#; serde_json::from_str::<Download>(s).unwrap(); } #[test] fn download_from_succeeded() { let s = r#"{"id":1,"status":"Successful"}"#; serde_json::from_str::<Download>(s).unwrap(); } #[test] fn download_from_failed() { let s = r#"{"id":1,"status":"Failed","cause":"Bad thing"}"#; serde_json::from_str::<Download>(s).unwrap(); } }
#[serde(flatten)]的作用是将枚举内部需要匹配的字段(包括status标签、Failed变体的cause字段)提升到和外层Download的id字段同级,反序列化时直接从JSON顶级字段匹配枚举需要的内容,完美适配你现有的JSON结构。修改后所有测试用例均可正常通过,且保留了类型安全的优势,不需要将cause定义为Option<String>。
内容的提问来源于stack exchange,提问作者Jack Johnstone
相关产品推荐
相关产品推荐

