10

I'm trying to get the name of an enum variant as the string serde would expect/create. For example, say I have the following enum:

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

How can I then get the serde names of the variants? Something like

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

I can use a hack with serde_json, for variants without a value I can do:

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

This is not the best solution as I then need to strip the quotation marks, but can technically work.

Even worse is when the enum variant has a value. It becomes much messier.

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

Is there a clean way to achieve this? I can't find a serde API to get key name as a string.

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Mendy
  • 7,612
  • 5
  • 28
  • 42
  • Doubtful, since the renamed string isn't really anything other than a hard-coded value in the expansion of `#[derive(Serialize, Deserialize)]`. What use do you have for this? Maybe there's another way of approaching the problem. – trent Apr 22 '21 at 15:25
  • Use case is having a js object with properties for each enum variant, and needing to set those properties with value of variant – Mendy Apr 22 '21 at 15:39

2 Answers2

7

A stable yet somewhat boilerplate heavy way of extracting the variant information is by implementing a custom Serializer which collects the variant names from the serialize_*_variant functions.

This is the approach taken by serde_variant. @Mendy mentioned that this crate only works for unit variants. This is the example in the 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");

One other downside to mention is that this only works with the default, externally tagged enum representation. Other representations do not use the serialize_*_variant functions.

jonasbb
  • 2,131
  • 1
  • 6
  • 25
1

When an Enum variant doesn't have a value, it will be serialized as a String, otherwise it will be serialized as an object with the variant name being the key.

Basically, your Enum would be serialized as follows:

#[derive(Serialize)]
struct MyStruct {
  my_field: SomeEnum,
  some_other_field: String,
};

Json example with the withValue variant:

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

Json example with the WithoutValue variant:

{
  "my_field": "withoutValue",
  "some_other_field": "Boom goes the dynamite"
}
John Kugelman
  • 349,597
  • 67
  • 533
  • 578
moy2010
  • 854
  • 12
  • 18
  • I didn't intent for my enum to be like Option, just wanted to demonstrate the difference from when the variant has a value to when it doesn't – Mendy Apr 22 '21 at 17:57
  • @Mendy, understood. I have edited my response accordingly. – moy2010 Apr 22 '21 at 18:07