How to deserialize a JSON file which contains null values using Serde?
Any field that can be null should be an Option
type so that you can handle the null case. Something like this?
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Element {
...
color: Option<String>,
...
}
A deserialization error occurs because the struct definition is incompatible with the incoming objects: the color
field can also be null
, as well as a string, yet giving this field the type String
forces your program to always expect a string. This is the default behaviour, which makes sense. Be reminded that String
(or other containers such as Box
) are not "nullable" in Rust. As for a null
value not triggering the default value instead, that is just how Serde works: if the object field wasn't there, it would work because you have added the default field attribute. On the other hand, a field "color" with the value null
is not equivalent to no field at all.
One way to solve this is to adjust our application's specification to accept null | string
, as specified by @user25064's answer:
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Element {
color: Option<String>,
}
Playground with minimal example
Another way is to write our own deserialization routine for the field, which will accept null
and turn it to something else of type String
. This can be done with the attribute #[serde(deserialize_with=...)]
.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Element {
#[serde(deserialize_with="parse_color")]
color: String,
}
fn parse_color<'de, D>(d: D) -> Result<String, D::Error> where D: Deserializer<'de> {
Deserialize::deserialize(d)
.map(|x: Option<_>| {
x.unwrap_or("black".to_string())
})
}
Playground
See also:
- How can I distinguish between a deserialized field that is missing and one that is null?
Based on code from here, when one needs default values to be deserialized if null
is present.
// Omitting other derives, for brevity
#[derive(Deserialize)]
struct Foo {
#[serde(deserialize_with = "deserialize_null_default")]
value: String,
}
fn deserialize_null_default<'de, D, T>(deserializer: D) -> Result<T, D::Error>
where
T: Default + Deserialize<'de>,
D: Deserializer<'de>,
{
let opt = Option::deserialize(deserializer)?;
Ok(opt.unwrap_or_default())
}
playground link with full example. This also works for Vec
and HashMap
.