如何为某些输入类型而不是所有输入类型自定义反序列化实现?
How do I customize a Deserialize implementation for certain input types but not all of them?
我有这样的类型,虽然我的实际类型更大更复杂:
struct MyType {
i: u32,
}
如果我为这种类型实现 Deserialize
,serde 会寻找这样的东西(我对 JSON 感兴趣):
{"i":100}
我想对其进行自定义,以便我也可以从字节数组反序列化:
[1, 2, 3, 4]
我可以编写一个 impl 来处理数组,但我希望 serde 自动生成其余部分(将是 visit_map
):
impl<'de> Deserialize<'de> for MyType {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct MyTypeVisitor;
impl<'de> Visitor<'de> for MyTypeVisitor {
type Value = MyType;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "struct or array of 4 integers")
}
fn visit_seq<A: SeqAccess<'de>>(self, seq: A) -> Result<Self::Value, A::Error> {
// ...
}
}
// deserializer.deserialize_any(MyTypeVisitor)
}
}
这可能吗?在这个例子中,这并不难,但是当结构很大时,手写反序列化会很痛苦。
这不是 How to transform fields during deserialization using Serde? 的副本,因为 deserialize_with
仅适用于 1 个字段。我不明白如何让它适用于我的真实类型:
pub enum Component {
String(StringComponent),
Translation(TranslationComponent),
Score(ScoreComponent),
Selector(SelectorComponent),
}
pub struct StringComponent {
#[serde(flatten)] pub base: Base,
pub text: String,
}
pub struct Base {
// ...
extra: Option<Vec<Component>>,
// ...
}
我想做的是:
- 反序列化时,如果输入是数字,return一个
Component::String
。这可以与 visit_i
/u
/f64
和朋友一起完成。
- 如果输入的是字符串,return再
Component::String
。这可以通过 visit_str
/string
. 来完成
- 如果输入是数组
[..]
,照常反序列化它,但将数组[1..]中的元素分配给数组[0]的额外元素。这可以通过 visit_seq
. 来完成
- 如果输入是映射,让 serde derive 处理它。
Serde documentation has an example showing how to implement deserializing from either a string or a structure。这相当于你的情况,只是更小。
重要的部分是:
fn visit_map<M>(self, visitor: M) -> Result<T, M::Error>
where
M: MapAccess<'de>,
{
Deserialize::deserialize(de::value::MapAccessDeserializer::new(visitor))
}
这委托给 built-in 反序列化实现。由于您所有其他案例都是自定义的,因此应该合适。
我有这样的类型,虽然我的实际类型更大更复杂:
struct MyType {
i: u32,
}
如果我为这种类型实现 Deserialize
,serde 会寻找这样的东西(我对 JSON 感兴趣):
{"i":100}
我想对其进行自定义,以便我也可以从字节数组反序列化:
[1, 2, 3, 4]
我可以编写一个 impl 来处理数组,但我希望 serde 自动生成其余部分(将是 visit_map
):
impl<'de> Deserialize<'de> for MyType {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
struct MyTypeVisitor;
impl<'de> Visitor<'de> for MyTypeVisitor {
type Value = MyType;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "struct or array of 4 integers")
}
fn visit_seq<A: SeqAccess<'de>>(self, seq: A) -> Result<Self::Value, A::Error> {
// ...
}
}
// deserializer.deserialize_any(MyTypeVisitor)
}
}
这可能吗?在这个例子中,这并不难,但是当结构很大时,手写反序列化会很痛苦。
这不是 How to transform fields during deserialization using Serde? 的副本,因为 deserialize_with
仅适用于 1 个字段。我不明白如何让它适用于我的真实类型:
pub enum Component {
String(StringComponent),
Translation(TranslationComponent),
Score(ScoreComponent),
Selector(SelectorComponent),
}
pub struct StringComponent {
#[serde(flatten)] pub base: Base,
pub text: String,
}
pub struct Base {
// ...
extra: Option<Vec<Component>>,
// ...
}
我想做的是:
- 反序列化时,如果输入是数字,return一个
Component::String
。这可以与visit_i
/u
/f64
和朋友一起完成。 - 如果输入的是字符串,return再
Component::String
。这可以通过visit_str
/string
. 来完成
- 如果输入是数组
[..]
,照常反序列化它,但将数组[1..]中的元素分配给数组[0]的额外元素。这可以通过visit_seq
. 来完成
- 如果输入是映射,让 serde derive 处理它。
Serde documentation has an example showing how to implement deserializing from either a string or a structure。这相当于你的情况,只是更小。
重要的部分是:
fn visit_map<M>(self, visitor: M) -> Result<T, M::Error>
where
M: MapAccess<'de>,
{
Deserialize::deserialize(de::value::MapAccessDeserializer::new(visitor))
}
这委托给 built-in 反序列化实现。由于您所有其他案例都是自定义的,因此应该合适。