如何将所有字段都是默认值的类型反序列化为 None ?

Posted

技术标签:

【中文标题】如何将所有字段都是默认值的类型反序列化为 None ?【英文标题】:How can I deserialize a type where all the fields are default values as a None instead? 【发布时间】:2019-03-07 18:02:32 【问题描述】:

我必须反序列化 JSON blob,在某些地方,整个对象的缺失被编码为具有相同结构的对象,但其所有字段都设置为默认值(空字符串和零)。

extern crate serde_json; // 1.0.27
#[macro_use] extern crate serde_derive; // 1.0.78
extern crate serde; // 1.0.78

#[derive(Debug, Deserialize)]
struct Test<T> 
    text: T,
    number: i32,


#[derive(Debug, Deserialize)]
struct Outer 
    test: Option<Test<String>>,


#[derive(Debug, Deserialize)]
enum Foo  Bar, Baz 
#[derive(Debug, Deserialize)]
struct Outer2 
    test: Option<Test<Foo>>,


fn main() 
    println!(":?", serde_json::from_str::<Outer>(r#" "test":  "text": "abc", "number": 42  "#).unwrap());
    // good: Outer  test: Some(Test  text: "abc", number: 42 ) 

    println!(":?", serde_json::from_str::<Outer>(r#" "test": null "#).unwrap());
    // good: Outer  test: None 

    println!(":?", serde_json::from_str::<Outer>(r#" "test":  "text": "", "number": 0  "#).unwrap());
    // bad: Outer  test: Some(Test  text: "", number: 0 ) 
    // should be: Outer  test: None 

    println!(":?", serde_json::from_str::<Outer2>(r#" "test":  "text": "Bar", "number": 42  "#).unwrap());
    // good: Outer2  test: Some(Test  text: Bar, number: 42 ) 

    println!(":?", serde_json::from_str::<Outer2>(r#" "test":  "text": "", "number": 0  "#).unwrap());
    // bad: error
    // should be: Outer  test: None 

我会在反序列化后处理这个问题,但正如您所见,这种方法不适用于枚举值:没有变体与空字符串匹配,因此反序列化完全失败。

如何教这个 serde?​​p>

【问题讨论】:

【参考方案1】:

你可以看看an example of custom field deserializing。

特别是,您可能想要定义类似

extern crate serde; // 1.0.78
#[macro_use]
extern crate serde_derive; // 1.0.78

use serde::Deserialize, Deserializer, de::Visitor;

fn none_if_all_default<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>
where
    T: Deserialize<'de>,
    D: Deserializer<'de> + Clone,

    struct AllDefault;

    impl<'de> Visitor<'de> for AllDefault 
        type Value = bool;

        // Implement the visitor functions here -
        // You can recurse over all values to check if they're
        // the empty string or 0, and return true
        //...
    

    let all_default = deserializer.clone().deserialize_any(AllDefault)?;

    if all_default 
        Ok(None)
     else 
        Ok(Some(T::deserialize(deserializer)?))
    

然后做

#[derive(Deserialize)]
struct Outer2 
    #[serde(deserialize_with = "none_if_all_default")]
    test: Option<Test<Foo>>,

【讨论】:

请增强您的示例,以显示如何按照 OP 的要求将其用于枚举。我无法让自己的示例与枚举一起使用。由于not all trait items implemented, missing: `expecting`,这也无法编译。 它无法编译,因为它并不意味着编译 - 这是一个框架答案,并且实现该特征是可以通过参考文档来完成的手动工作。重点是展示解决OP问题的高级想法。这适用于enum 字段,因为它适用于Option&lt;T&gt; 的任何字段。 deserialize_any consumes self。一旦它消失了,你如何打电话给T::deserialize?您的函数的返回类型不一致(Result::OkOption::Some)。这个答案不起作用。 反序列化器can not be cloned。请注意,这就是为什么强烈建议您在回答之前创建一个可行的解决方案的原因。

以上是关于如何将所有字段都是默认值的类型反序列化为 None ?的主要内容,如果未能解决你的问题,请参考以下文章

将 JSON 反序列化为具有通用字段的 Serializable 类 - 错误:不允许类型参数中的星形投影

如何使用 ISO 8601 格式的 DateTime 字段将 JSON 文本反序列化为 BsonDocument?

如何在数字反序列化中更改默认类型

如何将 JSON 反序列化为正确类型的对象,而无需事先定义类型?

如何将 XML Choice 反序列化为正确的复杂类型

将 false 反序列化为 null (System.Text.Json)