Issue
This Content is from Stack Overflow. Question asked by Roman Theuer
I am trying to get some data using Bungie API. There is last played date in json response in format like “2022-09-18T02:17:59Z”. I would like to parse it and create DateTime object.
I have following code (personal data replaced by XXX):
const API_ROOT: &str = "https://www.bungie.net/Platform";
const API_KEY_HEADER: &str = "X-API-Key";
const MY_API_KEY: &str = "XXX";
const MEMBERSHIP_TYPE: &str = "XXX";
const MEMBERSHIP_ID: &str = "XXX";
#[tokio::main]
async fn main() -> Result<(), reqwest::Error> {
let url = format!("{}/Destiny2/{}/Profile/{}/?components=100", API_ROOT, MEMBERSHIP_TYPE, MEMBERSHIP_ID);
let req = reqwest::Client::new()
.get(url)
.header(API_KEY_HEADER, MY_API_KEY)
.send()
.await?
.text()
.await?;
let data: JsonValue = serde_json::from_str(&req).expect("Failed to read json from response!");
let name = &data["Response"]["profile"]["data"]["userInfo"]["displayName"].to_string();
println!("Name: {}", name);
let last_played_raw = &data["Response"]["profile"]["data"]["dateLastPlayed"].to_string();
println!("{}", last_played_raw);
let last_played_raw = last_played_raw.replace("Z", "+00:00");
println!("{}", last_played_raw);
let last_played = DateTime::parse_from_rfc3339(&last_played_raw).unwrap();
println!("Last played: {}", last_played);
Ok(())
}
Problem is that parsing last_played_raw string panicks:
"2022-09-18T02:17:59Z"
"2022-09-18T02:17:59+00:00"
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: ParseError(Invalid)', srcmain.rs:37:70
Can somebody please tell me, what could be wrong?
Solution
The reason this happens is fairly simple. It happens because the leading and trailing quotation marks ("
) are included in the string that you passed into DateTime::parse_from_rfc3339()
. Probably the least incisive change to fix this would be to replace these with empty string literals like so:
extern crate serde_json;
extern crate serde;
use serde_json::Value as JsonValue;
use chrono::DateTime;
fn main() {
let json_string = r#"{"dateLastPlayed": "2022-09-18T02:17:59Z"}"#;
let data: JsonValue = serde_json::from_str(json_string).unwrap();
let last_played = data["dateLastPlayed"].to_string().replace("\"", "");
println!("{}", last_played.as_str());
let datetime = DateTime::parse_from_rfc3339(last_played.as_str()).expect("Failed to parse");
println!("{}", datetime);
}
Here is a link to the playground where you can see this easily.
However in serde
you typically don’t work with the raw Value
and instead handle deserializing like so:
use serde::Deserialize;
use chrono::DateTime;
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
struct MyData {
date_last_played: String,
}
fn main() {
let json_string = r#"{"dateLastPlayed": "2022-09-18T02:17:59Z"}"#;
let data: MyData = serde_json::from_str(json_string).unwrap();
let datetime = DateTime::parse_from_rfc3339(data.date_last_played.as_str()).expect("Failed to parse");
println!("{}", datetime);
}
Here is the playground for the above.
You should also be able to directly parse the JSON using this struct
struct MyData {
date_last_played: DateTime,
}
which would be the preferred choice, but you will need a custom serde Deserializer
for this.
This Question was asked in StackOverflow by Roman Theuer and Answered by frankenapps It is licensed under the terms of CC BY-SA 2.5. - CC BY-SA 3.0. - CC BY-SA 4.0.