关于rust:如何将自定义serde解串器用于chrono时间戳?

How to use a custom serde deserializer for chrono timestamps?

我正在尝试将JSON解析为具有chrono::DateTime字段的结构。 JSON具有以自定义格式保存的时间戳,我为此写了一个反序列化器。

如何连接两者并使用#[serde(deserialize_with)]使其正常工作?

我正在使用NaiveDateTime以获得更简单的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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);
}

我遇到了三种不同的编译错误:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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实现FromStr,这意味着您可以编写可重用的通用解串器函数。

一个令人费解的示例-确实在JSON中添加了以字符串形式表示的age字段(u8)。只是为了证明您可以将其用于实现FromStr

的任何内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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),则更加容易:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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 where D: Deserializer<'de>, although it may also be generic over T. Fields used with deserialize_with are not required to implement Deserialize.