如何从字符串或数字反序列化无字段枚举?

How can I deserialize a fieldless enum from either a string or number?

有没有一种简洁的方法可以从名称或判别值反序列化无字段枚举的变体?例如给定这个枚举——

enum Foo {
    A = 1,
    B = 2,
    C = 3,
}

—我想要这些字符串或数字中的任何一个来表示它:

{
  "example-a": "a", // Foo::A
  "example-b": "b", // Foo::B
  "example-c": "c", // Foo::C

  "example-1": 1, // Foo::A
  "example-2": 2, // Foo::B
  "example-3": 3, // Foo::C
}

我看到派生 Deserialize 包含前一组,Deserialize_repr 包含后者,但我不确定如何同时包含两者。

我预计可能会存在像 #[serde(alias = …)] 这样的 shorthand 来涵盖这种情况。

没有直接支持此功能的内置快捷方式。您将必须手动实施 Deserialize。它既不简单也不复杂:

impl<'de> serde::Deserialize<'de> for Foo {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>
    {
        struct FooVisitor;
        
        impl<'de> serde::de::Visitor<'de> for FooVisitor {
            type Value = Foo;
            
            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
                write!(formatter, "an integer or string representing a Foo")
            }
            
            fn visit_str<E: serde::de::Error>(self, s: &str) -> Result<Foo, E> {
                Ok(match s {
                    "a" => Foo::A,
                    "b" => Foo::B,
                    "c" => Foo::C,
                    _ => return Err(E::invalid_value(serde::de::Unexpected::Str(s), &self)),
                })
            }
            
            fn visit_u64<E: serde::de::Error>(self, n: u64) -> Result<Foo, E> {
                Ok(match n {
                    1 => Foo::A,
                    2 => Foo::B,
                    3 => Foo::C,
                    _ => return Err(E::invalid_value(serde::de::Unexpected::Unsigned(n), &self)),
                })
            }
        }
        
        deserializer.deserialize_any(FooVisitor)
    }
}

请注意,使用 deserialize_any 意味着我们依赖于自描述的数据格式;也就是说,反序列化器知道数据是字符串还是整数,并将相应地调用正确的 visit_ 方法。 Serde 还支持非自描述格式;但是,您将无法在此 Deserialize 实施中使用它们。