2017-06-02 28 views
6

我想使用Serde解析一些JSON作为HTTP PATCH请求的一部分。由于PATCH请求不会传递整个对象,只有相关的数据需要更新,所以我需要能够告诉未传递的值,明确设置为null的值和存在的值。如何区分缺失的反序列化字段和空字符?

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

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

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

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

我想改变aSome(42)bNone,并保持c不变。

我试图在Option一个多级包装的每个字段:

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

但是这并没有使bc之间的区别;都是None,但我想要bSome(None)

我没有绑定到嵌套Option s的这种表示形式;任何可以区分3种情况的解决方案都可以,例如使用自定义枚举的解决方案。

回答

5

很有可能,现在实现这一目标的唯一方法是使用自定义的反序列化函数。幸运的是,这并不难实现,甚至使其成为任何一种现场的工作:

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

Playground与完整的例子。输出:

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

大厦关闭的E_net4's answer,你还可以创建一个枚举了三种可能性:

#[derive(Debug)] 
enum Patch<T> { 
    Missing, 
    Null, 
    Value(T), 
} 

impl<T> Default for Patch<T> { 
    fn default() -> Self { 
     Patch::Missing 
    } 
} 

impl<T> From<Option<T>> for Patch<T> { 
    fn from(opt: Option<T>) -> Patch<T> { 
     match opt { 
      Some(v) => Patch::Value(v), 
      None => Patch::Null, 
     } 
    } 
} 

impl<'de, T> Deserialize<'de> for Patch<T> 
where 
    T: Deserialize<'de>, 
{ 
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> 
    where 
     D: Deserializer<'de>, 
    { 
     Option::deserialize(deserializer).map(Into::into) 
    } 
} 

这可以被用来作为:

#[derive(Debug, Deserialize)] 
struct ResourcePatch { 
    #[serde(default)] 
    a: Patch<i32>, 
} 

不幸的是,你仍然必须用#[serde(default)]注释每个字段(或将其应用于整个结构)。理想情况下,执行DeserializePatch将完全处理,但我还没有想出如何做到这一点。

+1

“理想情况下,为'Patch'执行'Deserialize'可以完全处理”我的假设到目前为止是不可能的。从您尝试反序列化“Patch”的那一刻起,您希望该值以其序列化形式存在。相反,'Patch :: Missing'通过它的“不存在”存在于我们的容器中(你实际上不能将Patch :: Missing'自己序列化为JSON)。 AFAIK a“序列化”不能选择不序列化,也不能告诉容器跳过该部分过程。 –

+0

其实,我以前的评论更适用于序列化,而不是反序列化。无论如何,逻辑有点双重:“反序列化”无法告诉“反序列化器”对它无法找到的值进行反序列化。如果我们有一个空的对象'{}','Deserialize'的实现无能为力,但是解串器可以在知道填充默认值之后。 –

相关问题