serde icon indicating copy to clipboard operation
serde copied to clipboard

Problem deserializing adjacently tagged enum with default content

Open psFried opened this issue 3 years ago • 3 comments
trafficstars

Example in playground

I have an adjacently tagged enum, and I'd like for the content to be optional. The enum looks like:

#[derive(Deserialize, Debug)]
#[serde(tag = "tag", content = "content")]
enum TestEnum {
    NoContent,
    OptionalContent(#[serde(default)] OptionalContent),
}

#[derive(Deserialize, Default, Debug)]
struct OptionalContent {
    _a: Option<String>,
}

I expected to be able to successfully deserialize the json {"tag": "OptionalContent"} into the OptionalContent variant and have the inner OptionalContent be populated with the default. The actual result was Err(Error("missing field content", line: 1, column: 26)).

When I change the input json to {"tag": "OptionalContent", "content": null}, I get a different error: Err(Error("invalid type: null, expected struct OptionalContent", line: 1, column: 42)).

I was able to get this to work is when the json is {"tag": "OptionalContent", "content": {}}, but I'd prefer to have the default work when content is either missing or null.

Is this a bug? Is there some other way to get this working? Appreciate whatever help or advice you're able to provide.

psFried avatar Jun 22 '22 16:06 psFried

I believe that this does work if you use Option<OptionalContent>. However it would be nice if this worked automatically if the variant has a Default.

sciyoshi avatar Oct 11 '22 01:10 sciyoshi

Related to #2248

kangalio avatar Oct 31 '22 20:10 kangalio

Probably, this could be solving by allowing #[serde(default)] on enum variants in adjacently tagged enums. Then in case of absence of the content field corresponding variant should be constructed using Default implementation of all it's components:

#[derive(Deserialize, Debug)]
#[serde(tag = "tag", content = "content")]
// Also should be possible to specify enum attribute instead
// of specifying attribute on each variant
// #[serde(default)]
enum AdjacentlyTagged {
  // result = Unit;
  #[serde(default)]
  Unit,

  // result = Newtype1(MustImplementDefault::default());
  #[serde(default)]
  Newtype1(MustImplementDefault),

  // result = Newtype2(d());
  #[serde(default)]
  Newtype2(#[serde(default = "d")] DoesNotImplementDefault),

  // result = Tuple(MustImplementDefault::default(), d());
  #[serde(default)]
  Tuple(MustImplementDefault, #[serde(default = "d")] DoesNotImplementDefault),

  // result = Struct { a: MustImplementDefault::default(), b: d() };
  #[serde(default)]
  Struct {
    a: MustImplementDefault,
    #[serde(default = "d")]
    b: DoesNotImplementDefault,
  },
}

Components should either implement Default, or have its own #[serde(default = "...")].

Mingun avatar Aug 11 '23 16:08 Mingun