如何使用可选的内部标记反序列化枚举?
How can I deserialize an enum with an optional internal tag?
我使用 Serde 反序列化一个用 YAML 编写的自定义配置文件。该文件可以包含我表示为内部标记枚举的各种定义:
OfKindFoo:
kind: Foo
bar: bar;
baz: baz;
OfKindQux:
kind: Qux
quux: qux;
我在 Rust 中是这样表示的:
#[derive(Deserialize)]
#[serde(tag = "kind")]
enum Definition {
Foo(Foo),
Qux(Qux),
}
#[derive(Deserialize)]
struct Foo {
bar: String,
baz: String,
}
#[derive(Deserialize)]
struct Qux {
quux: String,
}
我希望用户能够完全省略 kind
字段,当它被省略时,Serde 应该默认将其反序列化为 Foo
.
我在 Definition
上开始实施 Deserialize
。我正在尝试将其反序列化为地图,并根据此键以及它是否存在来查找 kind
键和 return 相应的枚举变体。
我需要以某种方式 "forward" 将其他地图字段反序列化为 Foo::deserialize
或 Bar::deserialize
。 fn deserialize
只接受一个参数,即 Deserializer
。有没有办法将地图 "convert" 放入反序列化器或以其他方式获得在该特定地图上 "starts" 的反序列化器?
我不能使用 #[serde(other)]
,因为它 returns Err
用于缺少标签。即使没有,文档也指出 other
只能应用于 "unit variant",一个不包含任何数据的变体。
您可以将主枚举标记为 untagged
并将标签添加到确实具有标签的子结构(此功能 is not documented,但是是故意添加的,因此似乎很可能会保留) .不过,没有标签的变体应该在其他变体之后声明,因为 serde 将尝试以 #[serde(untagged)]
声明的顺序反序列化变体。另请注意,如果在您的实际代码中,变体和结构具有不同的名称,或者您使用的是 #[serde(rename)]
,那么结构的名称对于(反)序列化很重要,而不是变体名称.适用于您的示例的所有内容:
#[derive(Deserialize)]
#[serde(untagged)]
enum Definition {
Qux(Qux),
Foo(Foo), // variant that doesn't have a tag is the last one
}
#[derive(Deserialize)]
struct Foo {
bar: String,
baz: String,
}
#[derive(Deserialize)]
#[serde(tag = "kind")]
// if you want the tag to be "qux" instead of "Qux", do
// #[serde(rename = "qux")]
// here (or add `, rename = "qux"` to the previous serde attribute)
struct Qux {
quux: String,
}
我使用 Serde 反序列化一个用 YAML 编写的自定义配置文件。该文件可以包含我表示为内部标记枚举的各种定义:
OfKindFoo:
kind: Foo
bar: bar;
baz: baz;
OfKindQux:
kind: Qux
quux: qux;
我在 Rust 中是这样表示的:
#[derive(Deserialize)]
#[serde(tag = "kind")]
enum Definition {
Foo(Foo),
Qux(Qux),
}
#[derive(Deserialize)]
struct Foo {
bar: String,
baz: String,
}
#[derive(Deserialize)]
struct Qux {
quux: String,
}
我希望用户能够完全省略 kind
字段,当它被省略时,Serde 应该默认将其反序列化为 Foo
.
我在 Definition
上开始实施 Deserialize
。我正在尝试将其反序列化为地图,并根据此键以及它是否存在来查找 kind
键和 return 相应的枚举变体。
我需要以某种方式 "forward" 将其他地图字段反序列化为 Foo::deserialize
或 Bar::deserialize
。 fn deserialize
只接受一个参数,即 Deserializer
。有没有办法将地图 "convert" 放入反序列化器或以其他方式获得在该特定地图上 "starts" 的反序列化器?
我不能使用 #[serde(other)]
,因为它 returns Err
用于缺少标签。即使没有,文档也指出 other
只能应用于 "unit variant",一个不包含任何数据的变体。
您可以将主枚举标记为 untagged
并将标签添加到确实具有标签的子结构(此功能 is not documented,但是是故意添加的,因此似乎很可能会保留) .不过,没有标签的变体应该在其他变体之后声明,因为 serde 将尝试以 #[serde(untagged)]
声明的顺序反序列化变体。另请注意,如果在您的实际代码中,变体和结构具有不同的名称,或者您使用的是 #[serde(rename)]
,那么结构的名称对于(反)序列化很重要,而不是变体名称.适用于您的示例的所有内容:
#[derive(Deserialize)]
#[serde(untagged)]
enum Definition {
Qux(Qux),
Foo(Foo), // variant that doesn't have a tag is the last one
}
#[derive(Deserialize)]
struct Foo {
bar: String,
baz: String,
}
#[derive(Deserialize)]
#[serde(tag = "kind")]
// if you want the tag to be "qux" instead of "Qux", do
// #[serde(rename = "qux")]
// here (or add `, rename = "qux"` to the previous serde attribute)
struct Qux {
quux: String,
}