serde
serde copied to clipboard
Cannot deserialize Adjacently-Tagged enums when content field is missing.
I have a struct comprised entirely of optional fields, which is represented as an Adjacently-Tagged enum. The JSON API hands back Adjacently-Tagged structures, however the Content field can be null
in some cases.
As an example, the API response can resemble:
{ "type": "foo", "content": { "bar": "baz" }},
{ "type": "foo", "content": null },
Ideally, this would deserialize into:
Example::Foo { bar: Some("baz") },
Example::Foo { bar: None },
Unfortunately, the JSON Deserializer fails on the null
Content field. As far as I can tell, there's no way to ignore this condition or use the default None values on Adjacently-Tagged enums.
use serde::Deserialize;
use serde_json;
#[derive(Debug, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case", tag = "t", content = "c")]
enum Example {
Foo { bar: Option<bool> },
Unit,
}
fn main() -> Result<(), serde_json::Error> {
let _summary = r#"[
{ "t": "foo", "c": { "bar": "baz"} },
{ "t": "foo", "c": { "bar": null } },
{ "t": "foo", "c": null }
]"#;
// When the entire object is fully-specified, bar is Some(String)
assert_eq!(
serde_json::from_str::<Example>(r#"{"t": "foo", "c": {"bar": true }}"#)?,
Example::Foo {
bar: Some(true)
}
);
// Field data is None when the bar property is null.
assert_eq!(
serde_json::from_str::<Example>(r#"{ "t": "foo", "c": {"bar": null }}"#)?,
Example::Foo { bar: None }
);
// Field data is None when the property is unspecified.
assert_eq!(
serde_json::from_str::<Example>(r#"{ "t": "foo", "c": {}}"#)?,
Example::Foo { bar: None }
);
// Unit variants handle this situation without issue
assert_eq!(
serde_json::from_str::<Example>(r#"{ "t": "unit", "c": null}"#)?,
Example::Unit
);
assert_eq!(
serde_json::from_str::<Example>(r#"{ "t": "unit" }"#)?,
Example::Unit
);
// Fails with an `"expected value"` error. Ideally, there would be a way to try
// returning Defaults or None, similar to the empty-object case above. It would
// likely be best if this behaviour could be controlled by attributes on the enum
// and variants.
assert_eq!(
serde_json::from_str::<Example>(r#"{ "t": "foo", "c": null }"#)?,
Example::Foo { bar: None },
);
Ok(())
}
As a workaround, I'm currently parsing the results into generic serde_json::Value
and doing a sanitization pass to replace the null
values with {}
. The resulting tree can subsequently be parsed with serde_json::from_value
, however this feels rather inelegant.