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"] }