I need to deserialize a JSON file into either None
or Some(T)
in Rust. The source we are using will provide null
or empty, '{}', JSON fields when no values are present. I want to handle both as the None
case and only deserialize when the JSON field is not null or empty.
input: {"test": null} -> output: {"test": None}
input: {"test": {}} -> output: {"test": None}
input: {"test": {"valid_json": 42}} -> output: {"test": {"valid_json": 42}}
All of the answers I could find address one case or another but not both.
use serde::{Deserialize, Deserializer};
#[derive(Deserialize, Debug, PartialEq)]
struct Foo {
#[serde(deserialize_with = "object_empty_as_none")]
bar: Option<Bar>,
}
#[derive(Deserialize, Debug, PartialEq)]
struct Bar {
inner: u32,
}
pub fn object_empty_as_none<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
where
D: Deserializer<'de>,
for<'a> T: Deserialize<'a>,
{
#[derive(Deserialize, Debug)]
#[serde(deny_unknown_fields)]
struct Empty {}
#[derive(Deserialize, Debug)]
#[serde(untagged)]
enum Aux<T> {
T(T),
Empty(Empty),
Null,
}
match Deserialize::deserialize(deserializer)? {
Aux::T(t) => Ok(Some(t)),
Aux::Empty(_) | Aux::Null => Ok(None),
}
}
fn main() {
let data = r#"{"bar": null}"#;
let v: Foo = serde_json::from_str(data).unwrap();
assert_eq!(v, Foo { bar: None });
let data = r#"{"bar": {}}"#;
let v: Foo = serde_json::from_str(data).unwrap();
assert_eq!(v, Foo { bar: None });
let data = r#"{"bar": {"inner": 42}}"#;
let v: Foo = serde_json::from_str(data).unwrap();
assert_eq!(
v,
Foo {
bar: Some(Bar { inner: 42 })
}
);
let data = r#"{"bar": {"not_inner": 42}}"#;
let v: Result<Foo, _> = serde_json::from_str(data);
assert!(v.is_err());
}
Should be enough for most case. Remove #[serde(deny_unknown_fields)]
on Empty
if you want to.