如何为计时时间戳使用自定义 serde 反序列化器?
Posted
技术标签:
【中文标题】如何为计时时间戳使用自定义 serde 反序列化器?【英文标题】:How to use a custom serde deserializer for chrono timestamps? 【发布时间】:2019-12-28 02:04:14 【问题描述】:我正在尝试将 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
,找到枚举std::result::Result
”可能意味着什么。
【问题讨论】:
【参考方案1】:这是相当复杂的,但以下工作:
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);
【讨论】:
谢谢。那么我的实现问题与预期的函数签名不完全匹配? (fn from_timestamp<'de, D>(d: D) -> Result<NaiveDateTime, D::Error> where D: de::Deserializer<'de>,
)
这是问题的一部分,是的。但这只是胶水。您可能会注意到真正的关键在于访问者特征的实现。这就是自定义序列化器/反序列化器在 serde 中的工作方式。【参考方案2】:
虽然@edwardw 的回答在技术上是正确的,但恕我直言,它包含太多样板。
NaiveDataTime
实现了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
实现的函数反序列化该字段。给定的函数必须可以作为fn<'de, D>(D) -> Result<T, D::Error> where D: Deserializer<'de>
调用,尽管它也可能是T
的通用函数。与deserialize_with
一起使用的字段不需要实现Deserialize
。
【讨论】:
以上是关于如何为计时时间戳使用自定义 serde 反序列化器?的主要内容,如果未能解决你的问题,请参考以下文章