Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get name of enum variant as string with serde

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.

like image 390
Mendy Avatar asked Apr 22 '21 15:04

Mendy


People also ask

Why can't I use [serde (tag ='...") on an ENUM with tuple variants?

This representation is common in Java libraries. This representation works for struct variants, newtype variants containing structs or maps, and unit variants but does not work for enums containing tuple variants. Using a # [serde (tag = "...")] attribute on an enum containing a tuple variant is an error at compile time.

What is the default representation of enum in serde?

The default representation for this enum in Serde is called the externally tagged enum representation. Written in JSON syntax it looks like: {"Request": {"id": "...", "method": "...", "params": {...}}}

Can You deserialize an ENUM with only one field?

Only allowed on a newtype variant (a tuple variant with only one field). Deserialize this variant if the enum tag is anything other than the tag of one of the other variants in this enum. Only allowed on a unit variant inside of an internally tagged or adjacently tagged enum.

How to convert a string value to enum type?

If you need to convert a string value to the enum type, then use Show activity on this post. Replace Your_ENUM_List in the code example with your ENUM and run this code. Show activity on this post. You may heard that enum.toString () has performance issue, so better to use nameof (enum.x), e.g nameof (ChangeFrequency.Always) will return "Always"


Video Answer


1 Answers

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.

like image 183
jonasbb Avatar answered Oct 17 '22 18:10

jonasbb