如何使用Serde使用自定义函数反序列化可选字段? [英] How can I deserialize an optional field with custom functions using Serde?

查看:472
本文介绍了如何使用Serde使用自定义函数反序列化可选字段?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想使用自定义功能对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);
}

我知道Serde可以很容易地反序列化Option<chrono::NaiveDate>,因为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.
 

解决方案

结构反序列化的默认行为是,当字段不以序列化形式出现时,为其分配具有各自默认值的字段.请注意,这与容器#[serde(default)]属性不同,该属性使用结构的默认值.

#[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类型的字段不再告诉反序列化器该字段可能不存在.相反,它表明存在一个字段,其内容可能为空或空(以Serde术语表示的none).例如,在serde_json中,Option<String>是与"nullstring"(TypeScript/Flow表示法中的null | string)等效的JavaScript.下面的代码可以与给定的定义和日期反序列化器一起正常工作:

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>,
}

游乐场

另请参阅:

I want to serialize and deserialize a chrono::NaiveDate with custom functions, but the Serde book does not cover this functionality and the code docs also do not help.

#[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);
}

I know that Option<chrono::NaiveDate> can be easily deserialized by Serde because Chrono has Serde support, but I'm trying to learn Serde so I want to implement it myself. When I run this code I have a error:

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.

解决方案

The default behaviour of struct deserialization is to assign fields with their respective default value when they are not present in their serialized form. Note that this is different from the container #[serde(default)] attribute, which fills in the fields with the struct's default value.

#[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 });

However, this rule changes when we use another deserializer function (#[serde(deserialize_with = "path")]). A field of type Option here no longer tells the deserializer that the field may not exist. Rather, it suggests that there is a field with possible empty or null content (none in Serde terms). In serde_json for instance, Option<String> is the JavaScript equivalent to "either null or string" (null | string in TypeScript / Flow notation). This code below works fine with the given definition and date deserializer:

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

Luckily, the deserialization process can become more permissive just by adding the serde(default) attribute (Option::default yields None):

#[derive(Debug, Serialize, Deserialize)]
struct Test {
    pub i: u64,

    #[serde(default)]
    #[serde(with = "date_serde")]
    pub date: Option<NaiveDate>,
}

Playground

See also:

这篇关于如何使用Serde使用自定义函数反序列化可选字段?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆