如何让Rust枚举以变体名为键反序列化YAML数据?
解决方案
要实现将YAML映射的键作为枚举变体的标识,你需要调整Serde的枚举反序列化策略,从依赖YAML标签(!csv)改为匹配单键映射结构。以下是两种可行方案:
方案一:直接修改枚举变体结构
将枚举变体改为单字段结构体,配合#[serde(untagged)]属性,让Serde自动匹配YAML中的键与变体字段名:
use serde::Deserialize; use serde_yaml; #[derive(Debug, Deserialize)] #[serde(rename_all = "snake_case")] #[serde(untagged)] pub enum Sink { Csv { csv: CsvSink }, Json { json: JsonSink }, } #[derive(Debug, Deserialize)] pub struct CsvSink { file_path: String, delimiter: char, append: bool } // 假设你还有JsonSink结构体 #[derive(Debug, Deserialize)] pub struct JsonSink { file_path: String, pretty: bool } // 测试代码 fn main() { let yaml = r#" csv: file_path: "tmp/output-data.csv" delimiter: , append: true "#; let sink: Sink = serde_yaml::from_str(yaml).unwrap(); println!("{:?}", sink); // 输出:Csv { csv: CsvSink { file_path: "tmp/output-data.csv", delimiter: ',', append: true } } }
#[serde(untagged)]会让Serde尝试将YAML映射与枚举的单字段结构体变体匹配:当映射中存在csv键时,就会将对应的值反序列化为CsvSink并包装到Sink::Csv变体中。
方案二:保留原枚举结构,使用中间转换类型
如果希望保持原枚举的元组结构体形式(Sink::Csv(CsvSink)),可以通过中间包装类型实现转换:
use serde::{Deserialize, Serialize}; use serde_yaml; #[derive(Debug, Deserialize)] #[serde(rename_all = "snake_case")] #[serde(from = "SinkWrapper")] pub enum Sink { Csv(CsvSink), Json(JsonSink), } #[derive(Debug, Deserialize)] pub struct CsvSink { file_path: String, delimiter: char, append: bool } #[derive(Debug, Deserialize)] pub struct JsonSink { file_path: String, pretty: bool } // 中间包装枚举,用于匹配YAML单键映射结构 #[derive(Debug, Deserialize)] #[serde(rename_all = "snake_case")] #[serde(untagged)] enum SinkWrapper { Csv { csv: CsvSink }, Json { json: JsonSink }, } // 实现从包装枚举到原枚举的转换 impl From<SinkWrapper> for Sink { fn from(wrapper: SinkWrapper) -> Self { match wrapper { SinkWrapper::Csv { csv } => Sink::Csv(csv), SinkWrapper::Json { json } => Sink::Json(json), } } } // 测试代码 fn main() { let yaml = r#" json: file_path: "tmp/output-data.json" pretty: true "#; let sink: Sink = serde_yaml::from_str(yaml).unwrap(); println!("{:?}", sink); // 输出:Json(JsonSink { file_path: "tmp/output-data.json", pretty: true }) }
#[serde(from = "SinkWrapper")]告诉Serde先将YAML反序列化为中间的SinkWrapper,再通过From转换为原Sink枚举,既保留了原枚举结构,又适配了目标YAML格式。
内容的提问来源于stack exchange,提问作者mtobin
相关产品推荐
相关产品推荐

