Get name of enum variant as string with serde
Asked Answered
I

2

14

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.

Ignatia answered 22/4, 2021 at 15:7 Comment(2)
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.Oilbird
Use case is having a js object with properties for each enum variant, and needing to set those properties with value of variantIgnatia
P
8

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.

Poltergeist answered 22/4, 2021 at 16:33 Comment(4)
This library is really cool but will not work with enums variants that have values, I upvoted it and it's too late to remove my voteIgnatia
@Ignatia Thanks, I added that as a limitation to my answer. I also explained how serde_variant works internally.Poltergeist
serde_variant is GPL-licensed :(Zuleika
Version 0.1.2 of serde_variant works on tuple and struct enum variants.Horrified
T
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"
}
Type answered 22/4, 2021 at 17:52 Comment(2)
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'tIgnatia
@Mendy, understood. I have edited my response accordingly.Type

© 2022 - 2025 — McMap. All rights reserved.