当前位置: 首页 > 面试题库 >

如何使用Serde使用自定义函数反序列化可选字段?

田瀚
2023-03-14
问题内容

我想chrono::NaiveDate使用自定义功能对a进行序列化和反序列化,但是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<chrono::NaiveDate>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.

问题答案:

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

#[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这里的类型字段不再告诉反序列化器该字段可能不存在。相反,它表明存在一个字段,其内容可能为空或空(none以Serde术语)。在serde_json,例如,Option<String>是JavaScript相当于“任一nullstring”(null | string在打字稿/流量表示)。下面的代码与给定的定义和日期反序列化器可以正常工作:

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::defaultyields None),反序列化过程就可以变得更加宽松:

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

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

操场



 类似资料:
  • 问题内容: 我有一些从Web服务返回的JSON数据。JSON是顶级数组: 使用make 可以对数组中包含的数据进行反序列化,但是,我无法让Serde对顶级数组进行反序列化。 我是否缺少某些内容,还是Serde不能对顶级数组进行反序列化? 问题答案: 您可以使用:

  • 问题内容: 我有一堂课 我想将下面的JSON数据反序列化到上面的类/对象中 我的想法是在JSON中是一个对象,但我只想获取(在JSON中)在反序列化期间将像在类中那样传递。 如何使用Json.NET实现该目标? 我相信我可以使用CustomJsonConverter完成它。但是我很困惑。docs中的示例仅用于,但不适用。 问题答案: 我只是使用上面在问题中提到的方法解决了我的问题。在我完整的代码下

  • 有没有一种方法可以指定不同的序列化/反序列化JSON字段名,而不必显式地写出getter和setter方法,或许可以使用lombok getter和setter? 与此示例类似,下面的代码允许将传入的JSON反序列化为不同的POJO字段名。它还会使POJO字段名序列化为: 但这当然是不成立的。

  • 问题内容: 我有以下课程,将其用作字典中的键: 我正在运行的测试在这里: 测试失败,因为Json.Net似乎正在使用字典键上的方法,而不是正确地序列化它们。上面测试得出的json是: 这显然是错误的。我如何使它工作? 问题答案: 这应该可以解决问题: 序列化: 通过调用,您正在序列化一个对象数组而不是字典。 反序列化: 在这里,您可以反序列化数组,然后通过调用检索字典。 我不确定输出是否满足您的期

  • 问题内容: 我正在使用Flickr API 。调用该方法时,默认的JSON结果为: 我想将此响应解析为Java对象: JSON属性应按以下方式映射: 不幸的是,我无法找到一种使用Annotations做到这一点的好方法。到目前为止,我的方法是将JSON字符串读入a 并从中获取值。 但是我认为,这是有史以来最不优雅的方式。有没有简单的方法,可以使用注释还是自定义反序列化器? 这对我来说将是很明显的,

  • 我需要编写一个方法,它接受一些对象、给定对象的类中存在的一些字段名和一些字段值。该值是字段的JSON序列化形式。该方法将获取该值并相应地反序列化它,如下所示: (我实际上只需要检索反序列化的值,而不需要重新设置它,但这使它成为一个更好的示例。)只要Jackson默认的反序列化足够,这就行了。现在让我们假设我有一个带有自定义(de)序列化程序的类: 一个可能的解决方案是手动检查注释。但是,我真的不想