如何区分缺失的反序列化字段和空字段? [英] How can I distinguish between a deserialized field that is missing and one that is null?
问题描述
我想使用 Serde 来解析一些 JSON 作为 HTTP PATCH 请求的一部分.由于 PATCH 请求不传递整个对象,只传递要更新的相关数据,因此我需要能够区分未传递的值、显式设置为 null
的值和存在的价值.
我有一个包含多个可为空字段的值对象:
结构资源{a:选项<i32>,b:选项<i32>,c:选项<i32>,}
如果客户端像这样提交JSON:
{"a": 42, "b": null}
我想把a
改为Some(42)
,b
改为None
,然后离开c
不变.
我尝试将每个字段包装在一层 Option
中:
#[derive(Debug, Deserialize)]结构资源补丁{a:选项<选项,b:选项<选项<i32>,c:选项<选项<i32>,}
游乐场>
这不区分b
和c
;两者都是 None
但我希望 b
是 Some(None)
.
我不依赖于嵌套 Option
的这种表示;任何可以区分这 3 种情况的解决方案都可以,例如使用自定义枚举的解决方案.
很可能,目前实现这一目标的唯一方法是使用自定义反序列化函数.幸运的是,它并不难实现,甚至可以使其适用于任何类型的领域:
fn deserialize_optional_field<'de, T, D>(deserializer: D) ->结果<Option<Option<T>,D::Error>在哪里D:解串器de",T:反序列化<'de>,{好的(一些(选项::反序列化(反序列化器)?))}
然后每个字段将被注释为:
#[serde(deserialize_with = "deserialize_optional_field")]a:选项<选项,
您还需要使用 #[serde(default)]
对结构进行注释,以便将空字段反序列化为 unwrapped" None代码>.诀窍是将当前值包裹在
Some
周围.
序列化依赖于另一个技巧:当字段为 None
时跳过序列化:
#[serde(deserialize_with = "deserialize_optional_field")]#[serde(skip_serializing_if = "Option::is_none")]a:选项<选项,
Playground 以及完整示例.输出:
原始 JSON: {"a": 42, "b": null}>资源 { 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>>,
}
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 Option
s; 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}
这篇关于如何区分缺失的反序列化字段和空字段?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!