json icon indicating copy to clipboard operation
json copied to clipboard

Flatten causes maps with integer keys to fail deserialization

Open Kixiron opened this issue 1 year ago • 1 comments

I'm honestly unsure whether this is a serde-json issue or a serde issue so feel free to redirect me if this is the wrong place.

But in the following code the addition of #[serde(flatten)] causes roundtrip deserialization to fail when it shouldn't

use serde::{Serialize, Deserialize};
use std::collections::BTreeMap;

#[derive(Debug, Deserialize, Serialize)]
struct OuterFlattened {
    // Without the flatten deserialization works, see Outer
    #[serde(flatten)]
    inner: Inner,
}

#[derive(Debug, Deserialize, Serialize)]
struct Outer {
    // Note that inner isn't flattened
    inner: Inner,
}

#[derive(Debug, Deserialize, Serialize)]
struct Inner {
    map: BTreeMap<u32, String>,
}

fn main() {
    let mut map = BTreeMap::new();
    map.insert(1, "a".to_owned());
    map.insert(2, "b".to_owned());
    map.insert(3, "c".to_owned());
    
    let outer = Outer {
        inner: Inner {
            map: map.clone(),
        },
    };
    
    let serialized = serde_json::to_string_pretty(&outer).unwrap();
    println!("Outer: {serialized}");

    // Works correctly
    let deserialized: Outer = serde_json::from_str(&serialized).unwrap();
    println!("Outer: {deserialized:?}");
    
    let outer_flattened = OuterFlattened {
        inner: Inner { map },
    };

    let serialized = serde_json::to_string_pretty(&outer_flattened).unwrap();
    println!("OuterFlattened: {serialized}");

    // Panics trying to deserialize an string integer key
    let deserialized: OuterFlattened = serde_json::from_str(&serialized).unwrap();
    println!("OuterFlattened: {deserialized:?}");
}

Output:

Outer: {
  "inner": {
    "map": {
      "1": "a",
      "2": "b",
      "3": "c"
    }
  }
}
Outer: Outer { inner: Inner { map: {1: "a", 2: "b", 3: "c"} } }
OuterFlattened: {
  "map": {
    "1": "a",
    "2": "b",
    "3": "c"
  }
}
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error("invalid type: string \"1\", expected u32", line: 7, column: 1)', src/main.rs:49:74

Playground Link

Removing the #[serde(flatten)] attribute from inner (as is done in Outer) makes it work correctly

Kixiron avatar Feb 24 '23 19:02 Kixiron