如何区分缺少的反序列化字段和不为null的字段? [英] How can I distinguish between a deserialized field that is missing and one that is null?

查看:291
本文介绍了如何区分缺少的反序列化字段和不为null的字段?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想使用 Serde 来解析某些JSON,并将其作为HTTP PATCH请求的一部分.由于PATCH请求不会传递整个对象,因此仅传递相关数据即可,因此我需要能够区分未传递的值,显式设置为null的值和存在的值.

我有一个带有多个可为空字段的值对象:

struct Resource {
    a: Option<i32>,
    b: Option<i32>,
    c: Option<i32>,
}

如果客户端提交这样的JSON:

{"a": 42, "b": null}

我想将a更改为Some(42),将b更改为None,并保持c不变.

我尝试将每个字段包装在Option的另一个级别中:

#[derive(Debug, Deserialize)]
struct ResourcePatch {
    a: Option<Option<i32>>,
    b: Option<Option<i32>>,
    c: Option<Option<i32>>,
}

游乐场

这不能区分bc;两者都是None,但我希望b成为Some(None).

我不依赖于嵌套Option的这种表示形式;可以区分这3种情况的任何解决方案都可以,例如使用自定义枚举的解决方案.

解决方案

很可能,目前实现此目标的唯一方法是使用自定义反序列化功能.幸运的是,实现它并不难,甚至使其适用于任何领域:

fn deserialize_optional_field<'de, T, D>(deserializer: D) -> Result<Option<Option<T>>, D::Error>
where
    D: Deserializer<'de>,
    T: Deserialize<'de>,
{
    Ok(Some(Option::deserialize(deserializer)?))
}

然后将对每个字段进行注释:

#[serde(deserialize_with = "deserialize_optional_field")]
a: Option<Option<i32>>,

您还需要使用#[serde(default)]注释该结构,以便将空字段反序列化为未包装" None.诀窍是将当前值包装在Some周围.

序列化依靠另一个技巧:当字段为None时跳过序列化:

#[serde(deserialize_with = "deserialize_optional_field")]
#[serde(skip_serializing_if = "Option::is_none")]
a: Option<Option<i32>>,

游乐场以及完整的示例.输出:

 Original JSON: {"a": 42, "b": null}
> Resource { a: Some(Some(42)), b: Some(None), c: None }
< {"a":42,"b":null}
 

I'd like to use Serde to parse some JSON as part of a HTTP PATCH request. Since PATCH requests don't pass the entire object, only the relevant data to update, I need the ability to tell between a value that was not passed, a value that was explicitly set to null, and a value that is present.

I have a value object with multiple nullable fields:

struct Resource {
    a: Option<i32>,
    b: Option<i32>,
    c: Option<i32>,
}

If the client submits JSON like this:

{"a": 42, "b": null}

I'd like to change a to Some(42), b to None, and leave c unchanged.

I tried wrapping each field in one more level of Option:

#[derive(Debug, Deserialize)]
struct ResourcePatch {
    a: Option<Option<i32>>,
    b: Option<Option<i32>>,
    c: Option<Option<i32>>,
}

playground

This does not make a distinction between b and c; both are None but I'd have wanted b to be Some(None).

I'm not tied to this representation of nested Options; any solution that can distinguish the 3 cases would be fine, such as one using a custom enum.

解决方案

Quite likely, the only way to achieve that right now is with a custom deserialization function. Fortunately, it is not hard to implement, even to make it work for any kind of field:

fn deserialize_optional_field<'de, T, D>(deserializer: D) -> Result<Option<Option<T>>, D::Error>
where
    D: Deserializer<'de>,
    T: Deserialize<'de>,
{
    Ok(Some(Option::deserialize(deserializer)?))
}

Then each field would be annotated as thus:

#[serde(deserialize_with = "deserialize_optional_field")]
a: Option<Option<i32>>,

You also need to annotate the struct with #[serde(default)], so that empty fields are deserialized to an "unwrapped" None. The trick is to wrap present values around Some.

Serialization relies on another trick: skipping serialization when the field is None:

#[serde(deserialize_with = "deserialize_optional_field")]
#[serde(skip_serializing_if = "Option::is_none")]
a: Option<Option<i32>>,

Playground with the full example. The output:

Original JSON: {"a": 42, "b": null}
> Resource { a: Some(Some(42)), b: Some(None), c: None }
< {"a":42,"b":null}

这篇关于如何区分缺少的反序列化字段和不为null的字段?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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