【问题标题】:How can I deserialize an optional field with custom functions using Serde?如何使用 Serde 反序列化带有自定义函数的可选字段?
【发布时间】:2017-06-01 07:57:05
【问题描述】:

我想用自定义函数对chrono::NaiveDate 进行序列化和反序列化,但 Serde 书并未涵盖此功能,代码文档也无济于事。

#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;
extern crate chrono;

use chrono::NaiveDate;


mod date_serde {
    use chrono::NaiveDate;
    use serde::{self, Deserialize, Serializer, Deserializer};

    pub fn serialize<S>(date: &Option<NaiveDate>, s: S) -> Result<S::Ok, S::Error>
    where S: Serializer {
        if let Some(ref d) = *date {
            return s.serialize_str(&d.format("%Y-%m-%d").to_string())
        }
        s.serialize_none()
    }

    pub fn deserialize<'de, D>(deserializer: D)
        -> Result<Option<NaiveDate>, D::Error>
        where D: Deserializer<'de> {
        let s: Option<String> = Option::deserialize(deserializer)?;
        if let Some(s) = s {
            return Ok(Some(NaiveDate::parse_from_str(&s, "%Y-%m-%d").map_err(serde::de::Error::custom)?))
        }

        Ok(None)
    }
}

#[derive(Debug, Serialize, Deserialize)]
struct Test {
    pub i: u64,
    #[serde(with = "date_serde")]
    pub date: Option<NaiveDate>,
}

fn main() {
    let mut test: Test = serde_json::from_str(r#"{"i": 3, "date": "2015-02-03"}"#).unwrap();
    assert_eq!(test.i, 3);
    assert_eq!(test.date, Some(NaiveDate::from_ymd(2015, 02, 03)));
    test = serde_json::from_str(r#"{"i": 5}"#).unwrap();
    assert_eq!(test.i, 5);
    assert_eq!(test.date, None);
}

我知道 Option&lt;chrono::NaiveDate&gt; 可以很容易地被 Serde 反序列化,因为 Chrono 支持 Serde,但是我正在尝试学习 Serde,所以我想自己实现它。当我运行此代码时出现错误:

thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: ErrorImpl { code: Message("missing field `date`"), line: 1, column: 8 }', /checkout/src/libcore/result.rs:859
note: Run with `RUST_BACKTRACE=1` for a backtrace.

【问题讨论】:

    标签: json serialization rust serde


    【解决方案1】:

    结构反序列化的默认行为是当字段不以序列化形式出现时,为其分配各自的默认值。请注意,这与 container #[serde(default)] attribute 不同,后者使用结构的默认值填充字段。

    #[derive(Debug, PartialEq, Deserialize)]
    pub struct Foo<'a> {
        x: Option<&'a str>,
    }
    
    let foo: Foo = serde_json::from_str("{}")?;
    assert_eq!(foo, Foo { x: None });
    

    但是,当我们使用另一个解串器函数 (#[serde(deserialize_with = "path")]) 时,此规则会发生变化。此处Option 类型的字段不再告诉反序列化器该字段可能不存在。相反,它表明存在一个可能为空或 null 内容的字段(在 Serde 术语中为none)。例如,在 serde_json 中,Option&lt;String&gt; 是 JavaScript 等价于“nullstring”(null | string 在 TypeScript / Flow 表示法中)。下面的代码适用于给定的定义和日期反序列化器:

    let test: Test = serde_json::from_str(r#"{"i": 5, "date": null}"#)?;
    assert_eq!(test.i, 5);
    assert_eq!(test.date, None);
    

    幸运的是,只需添加 serde(default) 属性(Option::default 产生 None),反序列化过程就可以变得更加宽松:

    #[derive(Debug, Serialize, Deserialize)]
    struct Test {
        pub i: u64,
    
        #[serde(default)]
        #[serde(with = "date_serde")]
        pub date: Option<NaiveDate>,
    }
    

    Playground

    另见:

    【讨论】:

      猜你喜欢
      • 2014-01-11
      • 1970-01-01
      • 2020-10-17
      • 2014-09-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-05-24
      相关资源
      最近更新 更多