如何将所有字段均为默认值的类型反序列化为 None?
How can I deserialize a type where all the fields are default values as a None instead?
我必须反序列化 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?
你可以看看an example of custom field deserializing.
特别是,您可能想要定义类似
的内容
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>>,
}
这里有两件事需要解决:如果value
都是默认值,则将Some(value)
替换为None
,并处理Foo
的空字符串情况.
第一件事很简单。如果输入字段不是 None
,Option
的 Deserialize
实现无条件地将其反序列化为 Some
,因此您需要创建一个自定义 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 } }
从 data-modeling 的角度来看,存在此问题的原因是它必须考虑到您会 json 的可能性:
{ "test": { "text": "", "number": 42 } }
在这种情况下,显然Outer { test: None }
不是正确的结果,但它仍然需要一个值存储在Foo
中,否则return反序列化错误。
如果您希望 ""
是有效文本 只有 如果 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}
,但无法反序列化 [=44] =].
同样,第三次,这个解决方案可能完全矫枉过正(特别是如果你的 real-world 用例涉及 Test
结构中的 2 个以上的字段),所以除非你有非常强烈的数据建模要求,您应该向 Foo
.
添加一个 Absent
变体
我必须反序列化 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?
你可以看看an example of custom field deserializing.
特别是,您可能想要定义类似
的内容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>>,
}
这里有两件事需要解决:如果value
都是默认值,则将Some(value)
替换为None
,并处理Foo
的空字符串情况.
第一件事很简单。如果输入字段不是 None
,Option
的 Deserialize
实现无条件地将其反序列化为 Some
,因此您需要创建一个自定义 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 } }
从 data-modeling 的角度来看,存在此问题的原因是它必须考虑到您会 json 的可能性:
{ "test": { "text": "", "number": 42 } }
在这种情况下,显然Outer { test: None }
不是正确的结果,但它仍然需要一个值存储在Foo
中,否则return反序列化错误。
如果您希望 ""
是有效文本 只有 如果 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}
,但无法反序列化 [=44] =].
同样,第三次,这个解决方案可能完全矫枉过正(特别是如果你的 real-world 用例涉及 Test
结构中的 2 个以上的字段),所以除非你有非常强烈的数据建模要求,您应该向 Foo
.
Absent
变体