简体   繁体   English

如何使用可选的内部标记反序列化枚举?

[英]How can I deserialize an enum with an optional internal tag?

I use Serde to deserialize a custom configuration file written in YAML. The file can contain definitions of various kinds that I represent as internally tagged enums:我使用 Serde 反序列化在 YAML 中编写的自定义配置文件。该文件可以包含我表示为内部标记枚举的各种定义:

OfKindFoo:
  kind: Foo
  bar: bar;
  baz: baz;

OfKindQux:
  kind: Qux
  quux: qux;

I represent it in Rust like this:我在 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,
}

I want the user to be able to omit the kind field completely, and when it is omitted Serde should default to deserializing it as Foo .我希望用户能够完全省略kind字段,当它被省略时,Serde 应该默认将其反序列化为Foo

I started to implement Deserialize on Definition .我开始在Definition上实施Deserialize I'm trying to deserialize it as a map and look for the kind key and return a respective enum variant based on this key and whether it is present.我正在尝试将其反序列化为 map 并查找kind键并根据此键及其是否存在返回相应的枚举变体。

I need to somehow "forward" the deserialization of other map fields to Foo::deserialize or Bar::deserialize , respectively.我需要以某种方式将其他 map 字段的反序列化分别“转发”到Foo::deserialize deserialize 或Bar::deserialize deserialize 。 fn deserialize only takes one argument which is Deserializer . fn deserialize只接受一个参数Deserializer Is there a way to "convert" the map into a deserializer or otherwise get a deserializer that "starts" on that particular map?有没有办法将 map“转换”为解串器或以其他方式获得在该特定 map 上“启动”的解串器?

I cannot use #[serde(other)] because it returns Err for a missing tag.我不能使用#[serde(other)]因为它返回缺少标签的Err Even if it didn't, the documentation states that other can only be applied to a "unit variant", a variant not containing any data.即使没有,文档也指出other只能应用于“单元变体”,即不包含任何数据的变体。

You can mark the main enum as untagged and add tags to the sub-structs that do have a tag (this feature is not documented , but was added deliberately and so seems likely to stay).您可以将主枚举标记为untagged并向具有标记的子结构添加标记(此功能未记录,但有意添加,因此似乎可能会保留)。 The variant without a tag should be declared after the other ones though, as serde will try to deserialize the variants in declared order with #[serde(untagged)] .不过,没有标签的变体应该在其他变体之后声明,因为 serde 将尝试使用#[serde(untagged)]以声明的顺序反序列化变体。 Also note that if in your actual code, the variants and the structs have different names, or you're using #[serde(rename)] , with this, the names of the structs are what matters for (de)serialization, not the variant names.另请注意,如果在您的实际代码中,变体和结构具有不同的名称,或者您正在使用#[serde(rename)] ,那么结构的名称对(反)序列化很重要,而不是变体名称。 All that applied to your example:所有适用于您的示例的内容:

#[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,
}

If structs have the same shape or all the fields are optional, accepted answer won't work and will be deserialized to the first kind.如果结构具有相同的形状或所有字段都是可选的,则接受的答案将不起作用,并将反序列化为第一种。 playground 操场

With the monostate crate it can be fixed.使用monostate crate 可以修复它。


use monostate::MustBe;
use serde::{Deserialize, Serialize};

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Action {
    Hi(Hi),
    Bye(Bye),
    Unknown(Unknown),
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Hi {
    kind: MustBe!("Hi"),
    name: String,
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Bye {
    kind: MustBe!("Bye"),
    name: String,
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
pub struct Unknown {
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn tests() {
        assert_eq!(
            Action::Hi(Hi { kind: Default::default(),  name: "John".to_string() }),
            serde_json::from_str::<Action>(r#"{"kind": "Hi", "name": "John"}"#).unwrap()
        );
        assert_eq!(
            Action::Bye(Bye { kind: Default::default(), name: "John".to_string() }),
            serde_json::from_str::<Action>(r#"{ "kind": "Bye", "name": "John" }"#).unwrap()
        );
        assert_eq!(
            Action::Unknown(Unknown {  }),
            serde_json::from_str::<Action>(r#"{ "name": "John" }"#).unwrap()
        );
        assert_eq!(
            Action::Unknown(Unknown {  }),
            serde_json::from_str::<Action>(r#"{}"#).unwrap()
        );
    }

}


声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM