【问题标题】:How can I implement serde for a type that I don't own and have it support compound /wrapper/collection types如何为我不拥有的类型实现 serde 并让它支持复合 /wrapper/collection 类型
【发布时间】:2020-12-13 19:18:42
【问题描述】:

这个问题类似 How do I implement a trait I don't own for a type I don't own?

我使用文档中描述的机制为 Date 编写了一个序列化程序,我的模块包装了一个序列化函数


pub mod my_date_format {
    use chrono::{Date, NaiveDate, Utc};
    use serde::{self, Deserialize, Deserializer, Serializer};

    const SERIALIZE_FORMAT: &'static str = "%Y-%m-%d";

    pub fn serialize<S>(date: &Date<Utc>, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let s = format!("{}", date.format(SERIALIZE_FORMAT));
        serializer.serialize_str(&s)
    }

    pub fn deserialize<'de, D>(deserializer: D) -> Result<Date<Utc>, D::Error>
    where
        D: Deserializer<'de>,
    {
        let s = String::deserialize(deserializer)?;
        NaiveDate::parse_from_str(s.as_str(), SERIALIZE_FORMAT)
            .map_err(serde::de::Error::custom)
            .map(|x| {
                let now = Utc::now();
                let date: Date<Utc> = Date::from_utc(x, now.offset().clone());
                date
            })
    }
}

那我可以做:

struct MyStruct {
    #[serde(with = "my_date_format")]
    pub start: Date<Utc>,
}

问题是如果我将序列化的东西包装成其他类型(它们本身是可序列化的)我会得到错误:

#[serde(with = "my_date_format")]
pub dates: Vec<Date<Utc> // this won't work now since my function doesn't serialize vectors
pub maybe_date: Option<Date<Utc>>> // won't work
pub box_date: Box<Date<Utc>> // won't work...

如何在使用我自己的序列化程序时获得提供的实现?

https://docs.serde.rs/serde/ser/index.html#implementations-of-serialize-provided-by-serde

【问题讨论】:

    标签: rust traits serde


    【解决方案1】:

    最直接的方法是按照question you linked 的说法,即创建一个新类型,包装Date&lt;Utc&gt;,并为该类型实现SerializeDeserialize

    #[derive(PartialOrd, Ord, PartialEq, Eq, Clone, Debug)]
    struct FormattedDate(Date<Utc>);
    
    impl Serialize for FormattedDate {
        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where
            S: Serializer,
        {
            // If you implement `Deref`, then you don't need to add `.0`
            let s = format!("{}", self.0.format(SERIALIZE_FORMAT));
            serializer.serialize_str(&s)
        }
    }
    
    impl<'de> Deserialize<'de> for FormattedDate {
        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where
            D: Deserializer<'de>,
        {
            let s = String::deserialize(deserializer)?;
            NaiveDate::parse_from_str(s.as_str(), SERIALIZE_FORMAT)
                .map_err(serde::de::Error::custom)
                .map(|x| {
                    let now = Utc::now();
                    let date: Date<Utc> = Date::from_utc(x, now.offset().clone());
                    Self(date)
                    // or
                    // date.into()
                })
        }
    }
    

    为了让生活更轻松,您可以实现DerefDerefMut,然后透明地使用FormattedDate,就像您直接使用Date&lt;Utc&gt;一样。

    use std::ops::{Deref, DerefMut};
    
    impl Deref for FormattedDate {
        type Target = Date<Utc>;
    
        fn deref(&self) -> &Self::Target {
            &self.0
        }
    }
    
    impl DerefMut for FormattedDate {
        fn deref_mut(&mut self) -> &mut Self::Target {
            &mut self.0
        }
    }
    

    同样你可以实现FromInto,这样你就可以很容易地在FormattedDateDate&lt;Utc&gt;之间进行转换。

    impl From<Date<Utc>> for FormattedDate {
        fn from(date: Date<Utc>) -> Self {
            Self(date)
        }
    }
    
    impl Into<Date<Utc>> for FormattedDate {
        fn into(self) -> Date<Utc> {
            self.0
        }
    }
    

    现在您提供的所有示例都易于使用:

    #[derive(Serialize, Deserialize, Debug)]
    struct MyStruct {
        date: FormattedDate,
        dates: Vec<FormattedDate>,
        opt_date: Option<FormattedDate>,
        boxed_date: Box<FormattedDate>,
    }
    
    fn main() {
        let s = MyStruct {
            date: Utc::now().date().into(),
            dates: std::iter::repeat(Utc::now().date().into()).take(4).collect(),
            opt_date: Some(Utc::now().date().into()),
            boxed_date: Box::new(Utc::now().date().into()),
        };
    
        let json = serde_json::to_string_pretty(&s).unwrap();
        println!("{}", json);
    }
    

    哪些输出:

    {
      "date": "2020-12-13",       
      "dates": [
        "2020-12-13",
        "2020-12-13",
        "2020-12-13",
        "2020-12-13"
      ],
      "opt_date": "2020-12-13",   
      "boxed_date": "2020-12-13"  
    }
    

    【讨论】:

    • 值得注意的是,如果您想保留包含“真实”日期对象的MyStruct,您可以创建一个带有FormattedDates 的MyStructRepresentation 结构并告诉serde 序列化和反序列化通过MyStructRepresentation
    【解决方案2】:

    不用依赖包装器类型,使用serde_with crate 中的serde_as 宏也可以获得相同的结果。 它的工作方式类似于带有属性的 serde,但也支持包装器和集合类型。

    由于您已经有了一个可以与 serde 一起使用的模块,因此困难的部分已经完成。 您可以在crate documentation 中找到详细信息。 您只需为特征 SerializeAsDeserializeAs 添加一个本地类型和两个样板实现即可使用您的自定义转换。

    use chrono::{Date, NaiveDate, Utc};
    
    struct MyDateFormat;
    
    impl serde_with::SerializeAs<Date<Utc>> for MyDateFormat {
        fn serialize_as<S>(value: &Date<Utc>, serializer: S) -> Result<S::Ok, S::Error>
        where
            S: serde::Serializer,
        {  
            my_date_format::serialize(value, serializer)
        }
    }
    
    impl<'de> serde_with::DeserializeAs<'de, Date<Utc>> for MyDateFormat {
        fn deserialize_as<D>(deserializer: D) -> Result<Date<Utc>, D::Error>
        where
            D: serde::Deserializer<'de>,
        {  
            my_date_format::deserialize(deserializer)
        }
    }
    
    #[serde_with::serde_as]
    #[derive(Serialize, Deserialize, Debug)]
    struct MyStruct {
        #[serde_as(as = "MyDateFormat")]
        date: Date<Utc>,
        #[serde_as(as = "Vec<MyDateFormat>")]
        dates: Vec<Date<Utc>>,
        #[serde_as(as = "Option<MyDateFormat>")]
        opt_date: Option<Date<Utc>>,
        #[serde_as(as = "Box<MyDateFormat>")]
        boxed_date: Box<Date<Utc>>,
    }
    
    fn main() {
        let s = MyStruct {
            date: Utc::now().date().into(),
            dates: std::iter::repeat(Utc::now().date().into()).take(4).collect(),
            opt_date: Some(Utc::now().date().into()),
            boxed_date: Box::new(Utc::now().date().into()),
        };
    
        let json = serde_json::to_string_pretty(&s).unwrap();
        println!("{}", json);
    }
    
    // This module is taken uunmodified from the question
    pub mod my_date_format {
        use chrono::{Date, NaiveDate, Utc};
        use serde::{self, Deserialize, Deserializer, Serializer};
    
        const SERIALIZE_FORMAT: &'static str = "%Y-%m-%d";
    
        pub fn serialize<S>(date: &Date<Utc>, serializer: S) -> Result<S::Ok, S::Error>
        where
            S: Serializer,
        {
            let s = format!("{}", date.format(SERIALIZE_FORMAT));
            serializer.serialize_str(&s)
        }
    
        pub fn deserialize<'de, D>(deserializer: D) -> Result<Date<Utc>, D::Error>
        where
            D: Deserializer<'de>,
        {
            let s = String::deserialize(deserializer)?;
            NaiveDate::parse_from_str(s.as_str(), SERIALIZE_FORMAT)
                .map_err(serde::de::Error::custom)
                .map(|x| {
                    let now = Utc::now();
                    let date: Date<Utc> = Date::from_utc(x, now.offset().clone());
                    date
                })
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2014-10-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多