Deserializing Dates with Serde


So, Let’s imagine some random data which contains a Date sitting on a remote server:

// sitting at https://api.dev
{
    "created_at": "1996-08-02 11:26:27", // rfc3339
}

I came across this while wanting to fetch data from APIs with Rust.

You just have to tell Rust how to deserialize through a “custom” deserializer. (Indeed, there’s a little bit of boilerplate code).

 0use chrono::{DateTime, NaiveDateTime, Utc};
 1use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};
 2
 3mod date_serializer {
 4fn time_to_json(t: NaiveDateTime) -> String {
 5DateTime::<Utc>::from_utc(t, Utc).to_rfc3339()
 6}
 7
 8    pub fn serialize<S: Serializer>(time: &NaiveDateTime, serializer: S) -> Result<S::Ok, S::Error> {
 9        time_to_json(time.clone()).serialize(serializer)
10    }
11
12    pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<NaiveDateTime, D::Error> {
13        let time: String = Deserialize::deserialize(deserializer)?;
14        Ok(NaiveDateTime::parse_from_str(&time, "%Y-%m-%d %H:%M:%S").map_err(D::Error::custom)?)
15    }
16
17}

Then, after fetching the data from the remote server, the deserialization is done transparently via a simple type annotation.

 0 #[macro_use]
 1extern crate failure;
 2extern crate reqwest;
 3extern crate chrono;
 4
 5use failure::Error;
 6
 7use reqwest::ClientBuilder;
 8use std::time::Duration;
 9
10#[derive(Deserialize, Debug)]
11pub struct Response { #[serde(with = "date_serializer")] // declaring custom deserializer
12pub created_at: NaiveDateTime,
13}
14
15fn main() -> Result<(), Error> {
16let request_url = "https://api.dev/";
17
18    let timeout = Duration::new(5, 0);
19    let client = ClientBuilder::new()
20            .timeout(timeout)
21            .build()
22            .map_err(|err|{
23                 format_err!("an error occured while creating reqwest HTTP Client: {}", err)
24            })?;
25
26    let mut http_response = client
27        .get(request_url)
28        .send()
29        .map_err(|err| {
30            format_err!("an error occured while calling remote API: {}", err)
31        })?;
32
33      if http_response.status().is_success() {
34            let response: Response = response.json().map_err(|err| {
35                format_err!("an error occured while unmarshalling response: {}", err)
36            })?;
37
38            println!("response: {:?}", response);
39
40            Ok(())
41        } else {
42            Err(format_err!(
43                "unsucessful response from API: {}",
44                resp.status()
45            ))
46        }
47
48}

Here are the versions of my dependencies at the time of writing. (1.28 Stable)

 0[dependencies]
 1serde = "1.0"
 2serde_json = "1.0"
 3serde_derive = "1.0"
 4
 5failure = "0.1"
 6failure_derive = "0.1"
 7
 8reqwest = "0.8"
 9
10chrono = { version = "0.4", features = ["serde"] }