serde序列化結構體中使用chrono的日期字段,i64轉換爲了日期字段例子

use chrono::prelude::*;
use serde::{Deserialize, Serialize};

fn main() {
    // let date: DateTime<Local> = Local::now();
    // println!("{}", date.format("%Y-%m-%d %H:%M:%S").to_string())

    // println!("{}",Local.timestamp_millis(1588325921426).format("%Y-%m-%d %H:%M:%S").to_string())

    let json_str = r#"
      {
        "name": "Skrillex",
        "create_time": 1588325921426//第三方對接時,對方給的時間爲時間戳格式
      }
    "#;

    let data: Person = serde_json::from_str(json_str).unwrap();
    println!("{:#?}", data);
    println!("{:#?}", data.create_time.format("%Y-%m-%d %H:%M:%S").to_string());

    let serialized = serde_json::to_string_pretty(&data).unwrap();
    println!("{}", serialized);
}

#[derive(Serialize, Deserialize, Debug)]
struct Person {
    name: String,
    #[serde(with = "my_date_format")]//序列化和反序列化時,調用my_date_format模塊中定義的序列化和反序列化方法來處理
    create_time: DateTime<Local>,

}

mod my_date_format {
    use std::error::Error;

    use chrono::{DateTime, Local, TimeZone};
    use serde::{self, Deserialize, Deserializer, Serializer};
    use serde::de::{Unexpected, Visitor};
    use serde::export::Formatter;

    pub fn serialize<S>(
        date: &DateTime<Local>,
        serializer: S,
    ) -> Result<S::Ok, S::Error>
        where
            S: Serializer,
    {
        serializer.serialize_i64(date.timestamp_millis())//序列化直接轉爲時間戳
    }


    pub fn deserialize<'de, D>(
        deserializer: D,
    ) -> Result<DateTime<Local>, D::Error>
        where
            D: Deserializer<'de>,
    {
        let timestamp = deserializer.deserialize_any(I64)?;//定義了I64訪問者來處理格式轉換
        Ok(Local.timestamp_millis(timestamp))
    }

    struct I64;

    impl<'de> Visitor<'de> for I64 {
        type Value = i64;
        fn expecting<'a>(&self, formatter: &mut Formatter<'a>) -> std::fmt::Result {
            write!(formatter, "is an integer")
        }

        fn visit_i32<E>(self, v: i32) -> Result<Self::Value, E> where
            E: Error, {//這個方法在這個業務裏沒有實現必要,只是爲了例子解釋
            println!("v {}", v);
            Ok(v as i64)
        }

        fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E> where
            E: Error, {
            println!("v {}", v);
            Ok(v)
        }

        fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E> where
            E: Error, {//這個例子如果不實現這個方法,就會報錯
            println!("v {}", v);
            Ok(v as i64)
        }
    }
}

serde框架使用了訪問者模式來定義數據處理【可以參考《Rust編程之道》中7.2.2訪問者模式中有對serde框架的源碼分析】。

先說下實現 impl<'de> Visitor<'de> for I64的默認轉換方法,剛開始我想着DateTime<Local>的timestamp_millis方法返回的時間戳是i64,所以只實現了visit_i64方法

fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E> where
    E: Error, 

,後來還是提示轉換失敗,就去看了Visitor<'de> 的源碼,發現visit_i8、visit_i16、visit_i32都是調用visit_i64方法,而visit_i64方法默認是返回一個error類型

    fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
    where
        E: Error,
    {
        Err(Error::invalid_type(Unexpected::Signed(v), &self))//重點在這裏 Unexpected類型不對
    }

但是我明明又重寫了visit_i64方法,但還是報這樣的錯誤。所以這時我就懷疑是不是serde框架先轉換成u64類型了。

同樣的,visit_u8、visit_u16、visit_u32都是調用visit_u64方法。我就懷疑serde框架是先調用了visit_u64方法,所以就重寫了visit_u64,加日誌發現對於"create_time": 1588325921426,1588325921426會優先解析爲u64類型,故只實現visit_u64方法即可。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章