Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Flatten causes maps with integer keys to fail deserialization #989

Open
Kixiron opened this issue Feb 24, 2023 · 1 comment
Open

Flatten causes maps with integer keys to fail deserialization #989

Kixiron opened this issue Feb 24, 2023 · 1 comment

Comments

@Kixiron
Copy link

Kixiron commented Feb 24, 2023

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

@jonasbb
Copy link

jonasbb commented Feb 25, 2023

serde has a long-standing bug where any internal buffering, such as the use of flatten or different enum representations, causes some features to fail. The serde issue is serde-rs/serde#1183. The issue here is that deserializing integers from strings only works for map keys, but that is a serde_json specialty. There are other issues with the same cause #496 or #560.

You can fix it by overwriting the deserialize implementation, for example using an extra crate:

#[serde_with::serde_as]
#[derive(Debug, Deserialize, Serialize)]
struct Inner {
    #[serde_as(as = "BTreeMap<serde_with::DisplayFromStr, _>")]
    map: BTreeMap<u32, String>,
}

Full example on Rustexplorer

elsirion added a commit to elsirion/fedimint that referenced this issue Sep 15, 2023
elsirion added a commit to elsirion/fedimint that referenced this issue Sep 15, 2023
shaurya947 pushed a commit to shaurya947/fedimint that referenced this issue Oct 13, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants