Deserialization using serde_yaml seems to be failing when it involves a nested struct

Deserialization using serde_yaml seems to be failing when it involves a nested struct something like below. Is this a bug or am I missing something here?
Cargo.toml

[package]
name = "random"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
serde_yaml = "0.9.27"
serde = { version = "1.0.123", features = ["derive"] }

Rust code

use serde::{Deserialize, Serialize};
use std::net::SocketAddr;

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
struct Config<A> {
    sync: Option<bool>,
    #[serde(flatten)]
    addition: A,
}

#[derive(Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "kebab-case")]
struct MyConfig {
    pub netconfig: Option<NetworkConfig>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
//#[serde(tag = "type", content = "value")]
pub enum NetworkConfig {
    Ethernet(SocketAddr),
}

// #[tokio::main]
fn main() {
    let config_str = r#"
netconfig: !ethernet 192.168.2.10:1234
"#;

    let value: serde_yaml::Value = serde_yaml::from_str(config_str).unwrap();
    println!("file-to-value \n {:?}", value);
    let config: MyConfig = serde_yaml::from_value(value.clone()).unwrap();
    println!("value-to-struct\n {:?}", config);
    // This below line fails
    let config: Config<MyConfig> = serde_yaml::from_value(value).unwrap();
    println!("value-to-struct\n {:?}", config);
}

Output

file-to-value 
 Mapping {"netconfig": TaggedValue { tag: !ethernet, value: String("192.168.2.10:1234") }}
value-to-struct
 MyConfig { netconfig: Some(Ethernet(192.168.2.10:1234)) }
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error("untagged and internally tagged enums do not support enum input")', src/main.rs:38:66
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

I don't think this is valid syntax. The following works:

let config_str = r#"
netconfig: 
  ethernet: 192.168.2.10:1234
"#;

Playground.

I think syntax I have used is valid one. The reason why your code works in playground is because playground uses lower crate version for serde_yaml. But my code is using the latest version of serde_yaml which is 0.9.27. Please check the Cargo.toml file in my code.

I have tried suggested changes on the latest version ("0.9.27") and it seems to be failing

file-to-value 
 Mapping {"netconfig": Mapping {"ethernet": String("192.168.2.10:1234")}}
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error("invalid type: map, expected a Value::Tagged enum")', src/main.rs:39:66
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

I tried using 0.9.0 specifically and it still fails, while 0.8.26 fails.

My guess is that this is due to a major change; I haven't gone through the serde_yaml patch notes for 0.9, but here they are; they may prove enlightening

1 Like

It is valid syntax. It's called a tag, and it can be used in YAML to denote the type of what would otherwise be a primitive value.

2 Likes

Sounds like [Bug] Newtype enum variant deserialization failure when used with `#[serde(flatten)]` · Issue #344 · dtolnay/serde-yaml · GitHub

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.