Search code examples
rustyamlserde

How to Serialize Rust Enum as YAML with Variant Name as Key?


I'm working with Rust to deserialize YAML data into an enum, but I want to use the key of a map in the YAML file as the tag which identifies the enum variant. Here's the relevant code:

#[derive(Debug, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Sink {
    Csv(CsvSink),
    Json(JsonSink),
}

#[derive(Debug, Deserialize)]
pub struct CsvSink {
    file_path: String,
    delimiter: char,
    append: bool
}

Currently, I can deserialize from this YAML file:

!csv
  file_path: "tmp/output-data.csv"
  delimiter: ,
  append: true

However, I want the YAML to look like this (i.e.:

csv:
  file_path: "tmp/output-data.csv"
  delimiter: ,
  append: true

How can I adjust my code to use the key in the yaml as the tag which determines the enum variant?

I have tried using different serde container attributes but have not found the right option


Solution

  • One of the breaking change in serde_yaml v0.9.0 is (de)serializing enum variants as ! tags instead of maps. The old behavior can be restored with serde_yaml::with::singleton_map, but this requires a wrapper type to apply the #[serde(with)] attribute to it:

    #[derive(Debug, Deserialize)]
    #[serde(transparent)]
    pub struct Wrapper(#[serde(with = "serde_yaml::with::singleton_map")] pub Sink);
    
    #[derive(Debug, Deserialize)]
    #[serde(rename_all = "snake_case")]
    pub enum Sink {
        Csv(CsvSink),
        Json(JsonSink),
    }
    
    #[derive(Debug, Deserialize)]
    pub struct CsvSink {
        file_path: String,
        delimiter: char,
        append: bool,
    }