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

如何让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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 14:47:34