serde icon indicating copy to clipboard operation
serde copied to clipboard

Apply rename_all to enum member "fields"

Open tazjin opened this issue 8 years ago • 5 comments

When using enums with struct-like members it would be useful if rename_all on the enclosing enum could apply to the members, too. For example:

#[serde(untagged, rename_all = "camelCase" )]
pub enum Value {
  Null { null_value: () },
  String { string_value: String },
  Boolean { boolean_value: bool },
}

could do

Value::String("foo".to_string())
=> 
{ "stringValue": "foo" }
  ------^-----
        |
This here is what I want

Currently the annotation has to be placed on the fields in addition:

#[serde(untagged)]
pub enum Value {
    Null {
        #[serde(rename = "nullValue")]
        null_value: ()
    },
    String {
        #[serde(rename = "stringValue")]
        string_value: String
    },
    /* etc. */
}

(If you're wondering what is going on with this type, it's Google Datastore).

tazjin avatar Oct 05 '17 19:10 tazjin

Seems reasonable. Do you have a suggestion for how this should work? Simplify changing the meaning of rename_all would be a breaking change.

Side note: representing the datastore value the following way may be more convenient. This avoids restating the type twice in Rust code -- Value::Boolean(true) instead of Value::Boolean { boolean_value: true }.

#[derive(Serialize, Deserialize)]
enum Value {
    #[serde(rename = "nullValue", with = "null")]
    Null,
    #[serde(rename = "stringValue")]
    String(String),
    #[serde(rename = "booleanValue")]
    Boolean(bool),
}

// Serialize the unit variant `Value::Null` as `{"nullValue":null}` rather
// than the default `"nullValue"`.
mod null {
    use serde::{Serialize, Serializer, Deserialize, Deserializer};

    pub fn serialize<S>(serializer: S) -> Result<S::Ok, S::Error>
        where S: Serializer
    {
        ().serialize(serializer)
    }
    
    pub fn deserialize<'de, D>(deserializer: D) -> Result<(), D::Error>
        where D: Deserializer<'de>
    {
        <()>::deserialize(deserializer)
    }
}

dtolnay avatar Oct 05 '17 20:10 dtolnay

Thanks for a quick reply! I'm really enjoying serde btw, easily the most pleasant serialisation lib I've worked with so far :-)


Do you have a suggestion for how this should work? Simplify changing the meaning of rename_all would be a breaking change.

Agreed, maybe it's sensible to have a separate rename_fields for this that would both apply to struct and enum-member fields?


Side note: representing the datastore value the following way may be more convenient

The problem is that the enum is "key-tagged", i.e. it's adjacent to other keys inside of a different struct and the key that contains the Value determines its type.

I haven't found a better way than the current untagged solution, but I also really need to stop thinking about this for today and find a nearby bed! ;-)

tazjin avatar Oct 05 '17 20:10 tazjin

Maybe instead of rename_all we should have had distinct rename_fields and rename_variants attributes, so that you can write both to get the desired effect.

It may be confusing to add those now and not have rename_all mean rename both fields and variants.

dtolnay avatar Oct 05 '17 21:10 dtolnay

I would be willing to consider a pull request that adds a serde(rename_all_fields = "...") attribute for enums that applies a rename rule to every field of every variant.

dtolnay avatar Jan 06 '19 05:01 dtolnay

@dtolnay Any update on this?

devashishdxt avatar Jun 06 '22 09:06 devashishdxt