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).

use chrono::{DateTime, NaiveDateTime, Utc};
use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};

mod date_serializer {
fn time_to_json(t: NaiveDateTime) -> String {
DateTime::<Utc>::from_utc(t, Utc).to_rfc3339()
}

    pub fn serialize<S: Serializer>(time: &NaiveDateTime, serializer: S) -> Result<S::Ok, S::Error> {
        time_to_json(time.clone()).serialize(serializer)
    }

    pub fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result<NaiveDateTime, D::Error> {
        let time: String = Deserialize::deserialize(deserializer)?;
        Ok(NaiveDateTime::parse_from_str(&time, "%Y-%m-%d %H:%M:%S").map_err(D::Error::custom)?)
    }

}

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


#[macro_use]
extern crate failure;
extern crate reqwest;
extern crate chrono;

use failure::Error;

use reqwest::ClientBuilder;
use std::time::Duration;

#[derive(Deserialize, Debug)]
pub struct Response { #[serde(with = "date_serializer")] // declaring custom deserializer
pub created_at: NaiveDateTime,
}

fn main() -> Result<(), Error> {
let request_url = "https://api.dev/";

    let timeout = Duration::new(5, 0);
    let client = ClientBuilder::new()
            .timeout(timeout)
            .build()
            .map_err(|err|{
                 format_err!("an error occured while creating reqwest HTTP Client: {}", err)
            })?;

    let mut http_response = client
        .get(request_url)
        .send()
        .map_err(|err| {
            format_err!("an error occured while calling remote API: {}", err)
        })?;

      if http_response.status().is_success() {
            let response: Response = response.json().map_err(|err| {
                format_err!("an error occured while unmarshalling response: {}", err)
            })?;

            println!("response: {:?}", response);

            Ok(())
        } else {
            Err(format_err!(
                "unsucessful response from API: {}",
                resp.status()
            ))
        }
}

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

[dependencies]
serde = "1.0"
serde_json = "1.0"
serde_derive = "1.0"

failure = "0.1"
failure_derive = "0.1"

reqwest = "0.8"

chrono = { version = "0.4", features = ["serde"] }
blog

© All rights reserved. Powered by Astro with ♥.