serde icon indicating copy to clipboard operation
serde copied to clipboard

#[serde(default = "...")] does not work on `flatten`-ed fields.

Open BertalanD opened this issue 5 years ago • 4 comments

I have a struct A that – along with other fields – contains a flattened B struct and a collection of B structs. I would like B's missing fields to take up a different default value if they are in the flattened field.

The problem

No matter what default attribute I set on the flatten-ed field, it will fall back to its type's default implementation.

For flattened structs, it would mean more sense that default applies to each field separately, as it's a way more common use case than wanting to fall back to the struct's Default implementation unless all of its fields are missing. But I am not even sure if that's how it's currently behaving.

Minimal example

These are my data structures:

#[derive(Serialize, Deserialize)]
#[serde(default)]
struct A {
    zero: usize,
    #[serde(flatten, default = "B::default_if_flattened")]
    b: B,
    c: Vec<B>,
}

impl Default for A {
    fn default() -> Self {
        A {
            zero: 0,
            b: B::default_if_flattened(),
            three: Vec::new(),
        }
    }
}

#[derive(Serialize, Deserialize)]
#[serde(default)]
struct B {
    one: Option<usize>,
    two: Option<String>
}

impl Default for B {
    fn default() -> Self {
        B {
            one: None,
            two: None
        }
    }
}

impl B {
    fn default_if_flattened() -> Self {
        B {
            one: Some(1),
            two: Some(String::from("two"))
        }
    }
}

And I want to deserialize the following TOML file:

zero = 0
two = "two"

three = [ { } ]

Expected results when deserialized into a value: A:

value.b.one == Some(1)
value.three[0].one == None

Here is the same thing in the Rust Playground (currently panics.)

BertalanD avatar Aug 18 '20 15:08 BertalanD

I seem to be running into the same issue with #[serde(default, flatten)] where my field type impls Default.

Any workarounds for this?

maxcountryman avatar Sep 17 '21 15:09 maxcountryman

Same thing, flatten doesn't work with default, nor with manual default trait impl nor with derive(default).

use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Debug, PartialEq)]
struct User {
    #[serde(flatten, default)]
    contact: Contact,
}

#[derive(Serialize, Deserialize, Debug, PartialEq, Default)]
struct Contact {
    email: String,
    phone: String,
}

fn main() {
    let json = r#"{
        "email": "[email protected]"
    }"#;

    let user: User = serde_json::from_str(json).unwrap();
    let expected_user = User { contact: Contact { email: "[email protected]".to_string(), phone: "".to_string() } };
    assert_eq!(user, expected_user);
}

panics with:

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Error("missing field `phone`", line: 3, column: 5)', src/main.rs:20:49

playground link

ByteNacked avatar Mar 28 '23 05:03 ByteNacked

This is duplicate of #1626

Mingun avatar Aug 11 '23 18:08 Mingun