Как использовать пользовательский десериализатор serde для хронологических меток времени

Я пытаюсь разобрать JSON в структуру, которая имеет chrono::DateTime поле. У json есть метки времени, сохраненные в пользовательском формате, для которого я написал десериализатор.

Как мне соединить два и заставить работать #[serde(deserialize_with)]?

Я использую NaiveDateTime для более простого кода

extern crate serde_json;      
extern crate serde;      
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<_, _>`

error: aborting due to 3 previous errors

For more information about this error, try `rustc --explain E0308`.
error: Could not compile `testcrate`.

To learn more, run the command again with --verbose.

Я уверен, что from_timestamp функция возвращает DateTime структура, а не Resultтак что я не знаю что такое "ожидаемая структура" chrono::NaiveDateTime, нашел enum std::result::Result"может означать.

2 ответа

Решение

Это довольно сложно, но следующие работы:

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 Это означает, что вы можете написать универсальную функцию десериализатора многократного использования.

Запутанный пример - действительно добавили age поле (u8) представлены в виде строки в формате JSON. Просто чтобы продемонстрировать, что вы можете использовать его для всего, что реализует 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,

Другие вопросы по тегам