Deserializing Dates in Rust with Serde
Fri Aug 17, 2018 · 351 words

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 REST 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 {
 4    fn time_to_json(t: NaiveDateTime) -> String {
 5        DateTime::<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}

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

back · blogging · who dis? · random stuff · main