使用serde将枚举变量的名称转换为字符串

11

我试图将枚举变量的名称作为serde期望/创建的字符串获取。例如,假设我有以下枚举:

#[derive(Serialize, Deserialize)]
#[serde(rename_all="camelCase")]
pub enum SomeEnum {
    WithoutValue,
    withValue(i32),
}

如何获取变体的serde名称?就像这样

serde::key_name(SomeEnum::WithoutValue) // should be `withoutValue`
serde::key_name(SomeEnum::WithValue)    // should be `withValue`

我可以使用 serde_json 的技巧,对于没有值的变体,我可以这样做:

serde_json::to_string(SomeEnum::WithoutValue).unwrap(); // yields `"withoutValue"` with quotation marks

这并不是最好的解决方案,因为我需要去掉引号,但从技术上讲可以工作。

更糟糕的是当枚举变量有一个值时,它会变得更加混乱。

serde_json::to_string(SomeEnum::WithValue(0)).unwrap(); // yields `"{\"second\":0}"

有没有一种简洁的方法来实现这个?我找不到一个能够获取键名作为字符串的serde API。


不确定,因为重命名的字符串实际上只是在 #[derive(Serialize, Deserialize)] 扩展中硬编码的值。你对此有什么用处?也许有另一种解决问题的方法。 - trent
使用案例是具有每个枚举变量属性的js对象,并需要将这些属性设置为变量的值。 - Mendy
2个回答

7
一种稳定但比较笨重的提取变量信息的方法是实现一个自定义的Serializer,从serialize_*_variant函数中收集变量名称。这是serde_variant采用的方法。@Mendy指出,此crate仅适用于单元变体。以下是Readme中的示例。
use serde_variant::to_variant_name;

#[derive(Serialize)]
enum Foo {
  Var1,
  #[serde(rename = "VAR2")]
  Var2,
}

assert_eq!(to_variant_name(&Foo::Var1).unwrap(), "Var1");
assert_eq!(to_variant_name(&Foo::Var2).unwrap(), "VAR2");

还有一个需要提到的缺点是,这只适用于默认的、外部标记的枚举表示。其他表示不使用serialize_*_variant函数。


这个库非常酷,但无法处理具有值的枚举变体,我已经点赞了,现在取消投票已经太晚了。 - Mendy
@Mendy 谢谢,我已将其作为我的答案限制。我还解释了serde_variant的内部工作原理。 - jonasbb
serde_variant 是以 GPL 许可证发布的 :( - andreymal

1
当枚举变量没有值时,它将被序列化为一个字符串;否则,它将被序列化为一个对象,其中变量名作为键。
基本上,您的枚举将被序列化如下:
#[derive(Serialize)]
struct MyStruct {
  my_field: SomeEnum,
  some_other_field: String,
};

使用withValue变量的Json示例:

{
  "my_field": {
    "withValue": 2
  },
  "some_other_field": "I like turtles"
}

使用WithoutValue变量的Json示例:

{
  "my_field": "withoutValue",
  "some_other_field": "Boom goes the dynamite"
}

我并不想让我的枚举像Option一样,只是想展示当变量有值和没有值时的区别。 - Mendy
@Mendy,明白了。我已经相应地编辑了我的回复。 - moy2010

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接