如何将自定义 serde 解串器用于计时时间戳?
How to use a custom serde deserializer for chrono timestamps?
我正在尝试将 JSON 解析为具有 chrono::DateTime
字段的结构。 JSON 以自定义格式保存时间戳,我为此编写了反序列化程序。
如何连接两者并使用 #[serde(deserialize_with)]
使其正常工作?
我使用 NaiveDateTime
来获得更简单的代码
extern crate serde;
extern crate serde_json;
use serde::Deserialize;
extern crate chrono;
use chrono::NaiveDateTime;
fn from_timestamp(time: &String) -> NaiveDateTime {
NaiveDateTime::parse_from_str(time, "%Y-%m-%dT%H:%M:%S.%f").unwrap()
}
#[derive(Deserialize, Debug)]
struct MyJson {
name: String,
#[serde(deserialize_with = "from_timestamp")]
timestamp: NaiveDateTime,
}
fn main() {
let result: MyJson =
serde_json::from_str(r#"{"name": "asdf", "timestamp": "2019-08-15T17:41:18.106108"}"#)
.unwrap();
println!("{:?}", result);
}
我遇到三个不同的编译错误:
error[E0308]: mismatched types
--> src/main.rs:11:10
|
11 | #[derive(Deserialize, Debug)]
| ^^^^^^^^^^^ expected reference, found type parameter
|
= note: expected type `&std::string::String`
found type `__D`
error[E0308]: mismatched types
--> src/main.rs:11:10
|
11 | #[derive(Deserialize, Debug)]
| ^^^^^^^^^^-
| | |
| | this match expression has type `chrono::NaiveDateTime`
| expected struct `chrono::NaiveDateTime`, found enum `std::result::Result`
| in this macro invocation
|
= note: expected type `chrono::NaiveDateTime`
found type `std::result::Result<_, _>`
error[E0308]: mismatched types
--> src/main.rs:11:10
|
11 | #[derive(Deserialize, Debug)]
| ^^^^^^^^^^-
| | |
| | this match expression has type `chrono::NaiveDateTime`
| expected struct `chrono::NaiveDateTime`, found enum `std::result::Result`
| in this macro invocation
|
= note: expected type `chrono::NaiveDateTime`
found type `std::result::Result<_, _>`
我很确定 from_timestamp
函数返回的是 DateTime
结构而不是 Result
,所以我不知道什么是“预期的结构 chrono::NaiveDateTime
, 发现 enum std::result::Result
" 可能意味着.
这相当复杂,但以下工作:
use chrono::NaiveDateTime;
use serde::de;
use serde::Deserialize;
use std::fmt;
struct NaiveDateTimeVisitor;
impl<'de> de::Visitor<'de> for NaiveDateTimeVisitor {
type Value = NaiveDateTime;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "a string represents chrono::NaiveDateTime")
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
match NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S.%f") {
Ok(t) => Ok(t),
Err(_) => Err(de::Error::invalid_value(de::Unexpected::Str(s), &self)),
}
}
}
fn from_timestamp<'de, D>(d: D) -> Result<NaiveDateTime, D::Error>
where
D: de::Deserializer<'de>,
{
d.deserialize_str(NaiveDateTimeVisitor)
}
#[derive(Deserialize, Debug)]
struct MyJson {
name: String,
#[serde(deserialize_with = "from_timestamp")]
timestamp: NaiveDateTime,
}
fn main() {
let result: MyJson =
serde_json::from_str(r#"{"name": "asdf", "timestamp": "2019-08-15T17:41:18.106108"}"#)
.unwrap();
println!("{:?}", result);
}
虽然@edwardw 的回答在技术上是正确的,但恕我直言包含太多样板文件。
NaiveDataTime
implements FromStr
这意味着您可以编写可重用的通用解串器函数。
一个令人费解的示例 - 确实在 JSON 中添加了表示为字符串的 age
字段 (u8
)。只是为了证明您可以将它用于任何实现 FromStr
.
的东西
use std::fmt::Display;
use std::str::FromStr;
use chrono::NaiveDateTime;
use serde::{de, Deserialize, Deserializer};
#[derive(Deserialize, Debug)]
struct MyJson {
name: String,
#[serde(deserialize_with = "deserialize_from_str")]
timestamp: NaiveDateTime,
#[serde(deserialize_with = "deserialize_from_str")]
age: u8,
}
// You can use this deserializer for any type that implements FromStr
// and the FromStr::Err implements Display
fn deserialize_from_str<'de, S, D>(deserializer: D) -> Result<S, D::Error>
where
S: FromStr, // Required for S::from_str...
S::Err: Display, // Required for .map_err(de::Error::custom)
D: Deserializer<'de>,
{
let s: String = Deserialize::deserialize(deserializer)?;
S::from_str(&s).map_err(de::Error::custom)
}
fn main() {
let result: MyJson = serde_json::from_str(
r#"{"name": "asdf", "timestamp": "2019-08-15T17:41:18.106108", "age": "11"}"#,
)
.unwrap();
println!("{:?}", result);
}
如果要指定格式就更简单了(使用NaiveDateTime::parse_from_str
):
use chrono::NaiveDateTime;
use serde::{de, Deserialize, Deserializer};
#[derive(Deserialize, Debug)]
struct MyJson {
name: String,
#[serde(deserialize_with = "naive_date_time_from_str")]
timestamp: NaiveDateTime,
}
fn naive_date_time_from_str<'de, D>(deserializer: D) -> Result<NaiveDateTime, D::Error>
where
D: Deserializer<'de>,
{
let s: String = Deserialize::deserialize(deserializer)?;
NaiveDateTime::parse_from_str(&s, "%Y-%m-%dT%H:%M:%S.%f").map_err(de::Error::custom)
}
fn main() {
let result: MyJson =
serde_json::from_str(r#"{"name": "asdf", "timestamp": "2019-08-15T17:41:18.106108"}"#)
.unwrap();
println!("{:?}", result);
}
#[serde(deserialize_with = "path")]
文档:
Deserialize this field using a function that is different from its implementation of Deserialize
. The given function must be callable as fn<'de, D>(D) -> Result<T, D::Error> where D: Deserializer<'de>
, although it may also be generic over T
. Fields used with deserialize_with
are not required to implement Deserialize
.
我正在尝试将 JSON 解析为具有 chrono::DateTime
字段的结构。 JSON 以自定义格式保存时间戳,我为此编写了反序列化程序。
如何连接两者并使用 #[serde(deserialize_with)]
使其正常工作?
我使用 NaiveDateTime
来获得更简单的代码
extern crate serde;
extern crate serde_json;
use serde::Deserialize;
extern crate chrono;
use chrono::NaiveDateTime;
fn from_timestamp(time: &String) -> NaiveDateTime {
NaiveDateTime::parse_from_str(time, "%Y-%m-%dT%H:%M:%S.%f").unwrap()
}
#[derive(Deserialize, Debug)]
struct MyJson {
name: String,
#[serde(deserialize_with = "from_timestamp")]
timestamp: NaiveDateTime,
}
fn main() {
let result: MyJson =
serde_json::from_str(r#"{"name": "asdf", "timestamp": "2019-08-15T17:41:18.106108"}"#)
.unwrap();
println!("{:?}", result);
}
我遇到三个不同的编译错误:
error[E0308]: mismatched types
--> src/main.rs:11:10
|
11 | #[derive(Deserialize, Debug)]
| ^^^^^^^^^^^ expected reference, found type parameter
|
= note: expected type `&std::string::String`
found type `__D`
error[E0308]: mismatched types
--> src/main.rs:11:10
|
11 | #[derive(Deserialize, Debug)]
| ^^^^^^^^^^-
| | |
| | this match expression has type `chrono::NaiveDateTime`
| expected struct `chrono::NaiveDateTime`, found enum `std::result::Result`
| in this macro invocation
|
= note: expected type `chrono::NaiveDateTime`
found type `std::result::Result<_, _>`
error[E0308]: mismatched types
--> src/main.rs:11:10
|
11 | #[derive(Deserialize, Debug)]
| ^^^^^^^^^^-
| | |
| | this match expression has type `chrono::NaiveDateTime`
| expected struct `chrono::NaiveDateTime`, found enum `std::result::Result`
| in this macro invocation
|
= note: expected type `chrono::NaiveDateTime`
found type `std::result::Result<_, _>`
我很确定 from_timestamp
函数返回的是 DateTime
结构而不是 Result
,所以我不知道什么是“预期的结构 chrono::NaiveDateTime
, 发现 enum std::result::Result
" 可能意味着.
这相当复杂,但以下工作:
use chrono::NaiveDateTime;
use serde::de;
use serde::Deserialize;
use std::fmt;
struct NaiveDateTimeVisitor;
impl<'de> de::Visitor<'de> for NaiveDateTimeVisitor {
type Value = NaiveDateTime;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "a string represents chrono::NaiveDateTime")
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
where
E: de::Error,
{
match NaiveDateTime::parse_from_str(s, "%Y-%m-%dT%H:%M:%S.%f") {
Ok(t) => Ok(t),
Err(_) => Err(de::Error::invalid_value(de::Unexpected::Str(s), &self)),
}
}
}
fn from_timestamp<'de, D>(d: D) -> Result<NaiveDateTime, D::Error>
where
D: de::Deserializer<'de>,
{
d.deserialize_str(NaiveDateTimeVisitor)
}
#[derive(Deserialize, Debug)]
struct MyJson {
name: String,
#[serde(deserialize_with = "from_timestamp")]
timestamp: NaiveDateTime,
}
fn main() {
let result: MyJson =
serde_json::from_str(r#"{"name": "asdf", "timestamp": "2019-08-15T17:41:18.106108"}"#)
.unwrap();
println!("{:?}", result);
}
虽然@edwardw 的回答在技术上是正确的,但恕我直言包含太多样板文件。
NaiveDataTime
implements FromStr
这意味着您可以编写可重用的通用解串器函数。
一个令人费解的示例 - 确实在 JSON 中添加了表示为字符串的 age
字段 (u8
)。只是为了证明您可以将它用于任何实现 FromStr
.
use std::fmt::Display;
use std::str::FromStr;
use chrono::NaiveDateTime;
use serde::{de, Deserialize, Deserializer};
#[derive(Deserialize, Debug)]
struct MyJson {
name: String,
#[serde(deserialize_with = "deserialize_from_str")]
timestamp: NaiveDateTime,
#[serde(deserialize_with = "deserialize_from_str")]
age: u8,
}
// You can use this deserializer for any type that implements FromStr
// and the FromStr::Err implements Display
fn deserialize_from_str<'de, S, D>(deserializer: D) -> Result<S, D::Error>
where
S: FromStr, // Required for S::from_str...
S::Err: Display, // Required for .map_err(de::Error::custom)
D: Deserializer<'de>,
{
let s: String = Deserialize::deserialize(deserializer)?;
S::from_str(&s).map_err(de::Error::custom)
}
fn main() {
let result: MyJson = serde_json::from_str(
r#"{"name": "asdf", "timestamp": "2019-08-15T17:41:18.106108", "age": "11"}"#,
)
.unwrap();
println!("{:?}", result);
}
如果要指定格式就更简单了(使用NaiveDateTime::parse_from_str
):
use chrono::NaiveDateTime;
use serde::{de, Deserialize, Deserializer};
#[derive(Deserialize, Debug)]
struct MyJson {
name: String,
#[serde(deserialize_with = "naive_date_time_from_str")]
timestamp: NaiveDateTime,
}
fn naive_date_time_from_str<'de, D>(deserializer: D) -> Result<NaiveDateTime, D::Error>
where
D: Deserializer<'de>,
{
let s: String = Deserialize::deserialize(deserializer)?;
NaiveDateTime::parse_from_str(&s, "%Y-%m-%dT%H:%M:%S.%f").map_err(de::Error::custom)
}
fn main() {
let result: MyJson =
serde_json::from_str(r#"{"name": "asdf", "timestamp": "2019-08-15T17:41:18.106108"}"#)
.unwrap();
println!("{:?}", result);
}
#[serde(deserialize_with = "path")]
文档:
Deserialize this field using a function that is different from its implementation of
Deserialize
. The given function must be callable asfn<'de, D>(D) -> Result<T, D::Error> where D: Deserializer<'de>
, although it may also be generic overT
. Fields used withdeserialize_with
are not required to implementDeserialize
.