dtolnay / serde-yaml

Strongly typed YAML library for Rust
Apache License 2.0
965 stars 164 forks source link

Error when deserializing sequence of untagged enum #361

Open kafkiansky opened 1 year ago

kafkiansky commented 1 year ago

On serde_yaml version 0.8 this code works fine:

use serde::{Deserialize};
use serde_yaml::{from_str};

#[derive(Debug, Deserialize)]
pub enum When {
    Conditions(Vec<When>),
    #[serde(rename = "active")]
    Active {
        until: i32,
    },
    #[serde(rename = "starts_with")]
    StartsWith {
        name: String,
    }
}

pub type Conditions = Vec<When>;

#[derive(Debug, Deserialize)]
pub struct Job {
    when: Conditions,
}

fn main() {
    let yaml = r#"
        when:
           - active: {until: 300}
           - starts_with: {name: "internal_*"}
    "#;

    let job: Job = from_str(yaml).unwrap();
    println!("{:#?}", job.when);
}

But on 0.9 raise an error:

"when[0]: invalid type: map, expected a YAML tag starting with '!'", line: 3, column: 14

Is this expected behavior or is it a bug?

mexus commented 1 year ago

This is probably what you want: https://docs.rs/serde_yaml/0.9.22/serde_yaml/with/singleton_map_recursive/index.html

I guess we need to apply a serde_yaml::with::singleton_map deserializer to the When struct, and in order to do so we can make a newtype for that purpose (the #[serde(with = ...)] won't work properly when applied to Vec<When>):

use serde::Deserialize;
use serde_yaml::from_str;

#[derive(Debug, Deserialize)]
pub enum When {
    Conditions(Vec<When>),
    #[serde(rename = "active")]
    Active {
        until: i32,
    },
    #[serde(rename = "starts_with")]
    StartsWith {
        name: String,
    },
}

pub type Conditions = Vec<When>;

#[derive(Debug, Deserialize)]
pub struct Job {
    #[serde(with = "serde_yaml::with::singleton_map_recursive")]
    when: Conditions,
}

fn main() {
    let yaml = r#"
        when:
           - active: {until: 300}
           - starts_with: {name: "internal_*"}
    "#;

    let job: Job = from_str(yaml).unwrap();
    println!("{:#?}", job.when);
}

There's also another custom (de)serializer https://docs.rs/serde_yaml/0.9.22/serde_yaml/with/singleton_map/index.html , but it would be more tricky to install (you're going to need a newtype or something, since it won't make any effect on Vec<T>).

Also see discussion at https://github.com/dtolnay/serde-yaml/issues/342

sambonbonne commented 10 months ago

I confirm I have the same bug in 0.9 but not in 0.8.