我使用 Serde 反序列化用 YAML 编写的自定义配置文件。该文件可以包含各种类型的定义,我表示为内部标记的枚举:
OfKindFoo:
kind: Foo
bar: bar;
baz: baz;
OfKindQux:
kind: Qux
quux: qux;
Run Code Online (Sandbox Code Playgroud)
我用 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,
}
Run Code Online (Sandbox Code Playgroud)
我希望用户能够kind
完全省略该字段,并且当它被省略时,Serde 应该默认将其反序列化为Foo
.
我开始Deserialize
在Definition
. 我正在尝试将其反序列化为映射并查找kind
键并根据此键以及它是否存在返回相应的枚举变体。
我需要以某种方式将其他地图字段的反序列化分别“转发”到Foo::deserialize
或Bar::deserialize
。fn deserialize
只接受一个参数,即Deserializer
. 有没有办法将地图“转换”为解串器或以其他方式获得在该特定地图上“开始”的解串器?
我无法使用,#[serde(other)]
因为它返回Err
缺少标签。即使没有,文档也指出other
只能应用于“单元变体”,即不包含任何数据的变体。
您可以将主枚举untagged
标记为并向具有标记的子结构添加标记(此功能未记录在案,但被有意添加,因此似乎可能会保留)。没有标签的变体应该在其他变体之后声明,因为 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,
}
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
528 次 |
最近记录: |