snippetrustMajor
How can I deserialize an optional field with custom functions using Serde?
Viewed 0 times
functionswithdeserializehowfieldserdeusingoptionalcancustom
Problem
I want to serialize and deserialize a
I know that
chrono::NaiveDate with custom functions, but the Serde book does not cover this functionality and the code docs also do not help.#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;
extern crate chrono;
use chrono::NaiveDate;
mod date_serde {
use chrono::NaiveDate;
use serde::{self, Deserialize, Serializer, Deserializer};
pub fn serialize(date: &Option, s: S) -> Result
where S: Serializer {
if let Some(ref d) = *date {
return s.serialize_str(&d.format("%Y-%m-%d").to_string())
}
s.serialize_none()
}
pub fn deserialize(deserializer: D)
-> Result, D::Error>
where D: Deserializer {
let s: Option = Option::deserialize(deserializer)?;
if let Some(s) = s {
return Ok(Some(NaiveDate::parse_from_str(&s, "%Y-%m-%d").map_err(serde::de::Error::custom)?))
}
Ok(None)
}
}
#[derive(Debug, Serialize, Deserialize)]
struct Test {
pub i: u64,
#[serde(with = "date_serde")]
pub date: Option,
}
fn main() {
let mut test: Test = serde_json::from_str(r#"{"i": 3, "date": "2015-02-03"}"#).unwrap();
assert_eq!(test.i, 3);
assert_eq!(test.date, Some(NaiveDate::from_ymd(2015, 02, 03)));
test = serde_json::from_str(r#"{"i": 5}"#).unwrap();
assert_eq!(test.i, 5);
assert_eq!(test.date, None);
}I know that
Option can be easily deserialized by Serde because Chrono has Serde support, but I'm trying to learn Serde so I want to implement it myself. When I run this code I have a error:thread 'main' panicked at 'called Result::unwrap() on an Err value: ErrorImpl { code: Message("missing field date"), line: 1, column: 8 }', /checkout/src/libcore/result.rs:859
note: Run with RUST_BACKTRACE=1 for a backtrace.
Solution
The default behaviour of struct deserialization is to assign fields with their respective default value when they are not present in their serialized form. Note that this is different from the container
However, this rule changes when we use another deserializer function (
Luckily, the deserialization process can become more permissive just by adding the
Playground
See also:
#[serde(default)] attribute, which fills in the fields with the struct's default value.#[derive(Debug, PartialEq, Deserialize)]
pub struct Foo {
x: Option,
}
let foo: Foo = serde_json::from_str("{}")?;
assert_eq!(foo, Foo { x: None });However, this rule changes when we use another deserializer function (
#[serde(deserialize_with = "path")]). A field of type Option here no longer tells the deserializer that the field may not exist. Rather, it suggests that there is a field with possible empty or null content (none in Serde terms). In serde_json for instance, Option is the JavaScript equivalent to "either null or string" (null | string in TypeScript / Flow notation). This code below works fine with the given definition and date deserializer:let test: Test = serde_json::from_str(r#"{"i": 5, "date": null}"#)?;
assert_eq!(test.i, 5);
assert_eq!(test.date, None);Luckily, the deserialization process can become more permissive just by adding the
serde(default) attribute (Option::default yields None):#[derive(Debug, Serialize, Deserialize)]
struct Test {
pub i: u64,
#[serde(default, with = "date_serde")]
pub date: Option,
}Playground
See also:
- How to deserialize a JSON file which contains null values using Serde?
- How can I distinguish between a deserialized field that is missing and one that is null?
Code Snippets
#[derive(Debug, PartialEq, Deserialize)]
pub struct Foo<'a> {
x: Option<&'a str>,
}
let foo: Foo = serde_json::from_str("{}")?;
assert_eq!(foo, Foo { x: None });let test: Test = serde_json::from_str(r#"{"i": 5, "date": null}"#)?;
assert_eq!(test.i, 5);
assert_eq!(test.date, None);#[derive(Debug, Serialize, Deserialize)]
struct Test {
pub i: u64,
#[serde(default, with = "date_serde")]
pub date: Option<NaiveDate>,
}Context
Stack Overflow Q#44301748, score: 83
Revisions (0)
No revisions yet.