Serde, remove tag when serializing for single enum variant

Hi Folks,

I have a an enum which has a single tuple variant, short example:

#[derive(Serialize)]
enum Test {
    A,
    B(String),
}

I'd like Test::B to be serialized untagged, so only the String value it holds gets serialized - serializing Test::B("some string".to_owned()) will return "some string". Setting #[serde(untagged)] won't work as it makes Test::A serialize to null, I also tried using serialize_with on that variant, but realized that only gives access to the String field in that variant.

I'm wondering if anyone knows is it even possible to do this without resorting to writing a fully custom implementation of Serialize? I want to avoid that as the real enum has many other unit like variants (see octocrab/events.rs at master · XAMPPRocky/octocrab · GitHub).

This playground link should make it clearer Rust Playground.

Thanks!

Instead of writing a custom Serialize implementation, it is also possible to wrap the Serializer to get the desired results. I'm not sure if that is easier overall. Playground
These are the relevant lines:

    fn serialize_newtype_variant<T: ?Sized>(
        self,
        name: &'static str,
        variant_index: u32,
        variant: &'static str,
        value: &T,
    ) -> Result<Self::Ok, Self::Error>
    where
        T: Serialize,
    {
        value.serialize(self.0)
    }

The main benefit is that it works without knowledge of the enum and it can be reused for as many enums as desired.

The other option is that you use a different derive macro instead of serde_derive.

Ah thanks! That makes sense, I didn't even think of that. The only downside is having all the unimplemented trait methods there. But it's not so bad.

I overlooked another solution too which I think I will go with. The main concern I had with implementing a custom Serializer was just repeating the enum variant names as strings constantly, error prone. I didn't event think of using a macro :grinning_face_with_smiling_eyes:

So, I think something like this solves my main problem:

macro_rules! event_type {
    ( $( $name:ident ),+ $(,)? ) => {
        /// The type of an event.
        #[derive(Debug, Clone, PartialEq, Deserialize)]
        #[non_exhaustive]
        pub enum EventType {
            $($name),+,
            UnknownEvent(String),
        }

        impl Serialize for EventType {
            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
            where
                S: serde::Serializer,
            {
                match self {
                    $(EventType::$name => serializer.serialize_str(stringify!($name))),+,
                    EventType::UnknownEvent(typ) => serializer.serialize_str(typ),
                }
            }
        }
        ...
    };
}

Turns out this also helps with repetition in other places too.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.