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方法即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值