Search code examples
serializationstructrustenumsserde

How to serialise only name of variant and ignore value for enum field in struct


Given the definitions:

#[derive(Serialize, Deserialize)]
enum Bar {
  A(i64),
  B(u64),
}

#[derive(Serialize, Deserialize)]
struct Foo {
  bar: Bar,
}

the JSON serialisation for

Foo {
  bar: Bar::A(123),
}

would be:

{
  "bar": "A"
}

It would be ideal to add an attribute to the field in the struct instead of inside the enum definition (the enum will be reused in a struct field where the value needs to be serialised too)


Solution

  • The attribute #[serde(skip)] can be used on tuple variant fields:

    use serde::{Serialize, Deserialize}; // 1.0.126
    use serde_json; // 1.0.64
    
    #[derive(Serialize, Deserialize)]
    enum Bar {
        A(#[serde(skip)] i64),
        B(#[serde(skip)] u64),
    }
    
    #[derive(Serialize, Deserialize)]
    struct Foo {
        bar: Bar,
    }
    
    fn main() {
        let foo = Foo { bar: Bar::A(123) };
        println!("{}", serde_json::to_string(&foo).unwrap());
    }
    
    {"bar":"A"}
    

    If modifying Bar isn't an option, you'll have to do it a bit more manually via #[serde(with = ...)] or #[serde(serialize_with = ...)]:

    use serde::{Serialize, Deserialize, ser::Serializer}; // 1.0.126
    use serde_json; // 1.0.64
    
    #[derive(Serialize, Deserialize)]
    enum Bar {
        A(i64),
        B(u64),
    }
    
    #[derive(Serialize, Deserialize)]
    struct Foo {
        #[serde(serialize_with = "bar_name_only")]
        bar: Bar,
    }
    
    fn bar_name_only<S>(bar: &Bar, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let name = match bar {
            Bar::A(_) => "A",
            Bar::B(_) => "B",
        };
    
        serializer.serialize_str(name)
    }
    
    fn main() {
        let foo = Foo { bar: Bar::A(123) };
        println!("{}", serde_json::to_string(&foo).unwrap());
    }
    
    {"bar":"A"}