我必须反序列化 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>
最佳答案
这里有两件事需要解决:替换Some(value)
与 None
如果value
都是默认值,并处理 Foo
的空字符串大小写.
第一件事很简单。 Deserialize
Option
的实现无条件反序列化为 Some
如果输入字段不是 None
, 所以你需要创建一个自定义 Deserialize
替换 Some(value)
的实现与 None
如果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
中, 否则返回反序列化错误。
如果你希望它成为 ""
的情况如果number
是有效文本仅是0
,与仅使用 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
的变体.
关于json - 如何将所有字段都是默认值的类型反序列化为 None ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52611244/