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



我必须反序列化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 }
}

我会在反序列化后处理这个问题,但正如你所看到的,这种方法对于枚举值是不可能的:没有变体与空字符串匹配,所以反序列化完全失败。

我怎么能教这个给塞尔德?

这里有两件事需要解决:如果value都是默认值,则用None替换Some(value),以及处理Foo的空字符串大小写。

第一件事很简单。如果输入字段未None,则OptionDeserialize实现无条件地将其反序列化为Some,因此您需要创建一个自定义Deserialize实现,如果value等于某个哨兵,则用None替换Some(value),例如默认值(这是 Issac 提出的答案, 但在这里正确实现(:

fn none_if_all_default<'de, T, D>(deserializer: D) -> Result<Option<T>, D::Error>
where
T: Deserialize<'de> + Default + Eq,
D: Deserializer<'de>,
{
Option::deserialize(deserializer).map(|opt| match opt {
Some(value) if value == T::default() => None,
opt => opt,
})
}
#[derive(Deserialize)]
struct Outer<T: Eq + Default> {
#[serde(deserialize_with = "none_if_all_default")]
#[serde(bound(deserialize = "T: Deserialize<'de>"))]
test: Option<Test<T>>,
}

这解决了您问题的前半部分,Option<Test<String>>.这将适用于Eq + Default的任何可反序列化类型。

enum的情况要棘手得多;您面临的问题是Foo根本不会从"Bar""Baz"以外的字符串反序列化。除了在枚举中添加第三个"死"变体之外,我真的没有看到一个好的解决方案:

#[derive(PartialEq, Eq, Deserialize)]
enum Foo {
Bar,
Baz,
#[serde(rename = "")]
Absent,
}
impl Default for Foo { fn default() -> Self { Self::Absent } }

从数据建模的角度来看,这个问题存在的原因是它必须考虑你得到像这样 json 的可能性:

{ "test": { "text": "", "number": 42 } }

在这种情况下,显然Outer { test: None }不是正确的结果,但它仍然需要一个值来存储在Foo中,否则返回反序列化错误。

如果您希望仅在0number""时才是有效文本,与仅使用Absent相比,您可以做一些精细且可能过度满足您的需求的事情。您需要使用未标记的枚举,它可以存储"有效"Test或"全部为空"Test,然后创建反序列化默认值的结构版本:

struct MustBeDefault<T> {
marker: PhantomData<T>
}
impl<'de, T> Deserialize<'de> for MustBeDefault<T>
where
T: Deserialize<'de> + Eq + Default
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>
{
match T::deserialize(deserializer)? == T::default() {
true => Ok(MustBeDefault { marker: PhantomData }),
false => Err(D::Error::custom("value must be default"))
}
}
}
// All fields need to be generic in order to use this solution.
// Like I said, this is radically overkill.
#[derive(Deserialize)]
struct Test<T, U> {
text: T,
number: U,
}
#[derive(Deserialize)]
#[serde(untagged)]
enum MaybeDefaultedTest<T> {
AllDefault(Test<EmptyString, MustBeDefault<i32>>),
Normal(Test<Foo, i32>),
}
// `EmptyString` is a type that only deserializes from empty strings;
// its implementation is left as an exercise to the reader.
// You'll also need to convert from MaybeDefaultedTest<T> to Option<T>;
// this is also left as an exercise to the reader.

现在可以编写MaybeDefaulted<Foo>,它将从{"text": "", "number": 0}{"text": "Baz", "number": 10}{"text": "Baz", "number": 0}之类的东西反序列化,但无法从{"text": "", "number": 10}反序列化。

同样,第三次,这个解决方案可能从根本上矫枉过正(特别是如果你的实际用例涉及Test结构中的 2 个以上的字段(,所以除非你有非常强烈的数据建模要求,否则你应该添加一个Absent变体到Foo

您可以查看自定义字段反序列化的示例。

特别是,您可能希望定义类似

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

最新更新