serde-rs/serde

Serde rename field attribute only for Serialize or Deserialize

deankarn opened this issue · 3 comments

Hi, first off Serde is amazing!

I came across a situation where I needed to rename a field for Deserialization and so came across the rename file attribute; the only issue is that I do not want to rename the Serialize, but use the rust name.

The situation I am using this in is accepting input from a legacy API and so want to remap the old names, however when serializing the same data to be stored/used in a new piece of code I didn't see a way to rename just Serialize or Deserialize and not both;

Is this possible? or could an option be added like rename_serialize & rename_deserialize?

it's possible I have missed this functionlity in the docs, thanks for any help :)

The rename attribute supports passing distinct serialize and deserialize renames. I was not able to find documentation of this either so I filed serde-rs/serde-rs.github.io#77 to follow up.

#[macro_use]
extern crate serde_derive;

extern crate serde;
extern crate serde_json;

#[derive(Serialize, Deserialize)]
struct Joeybloggs {
    #[serde(rename(deserialize = "old", serialize = "new"))]
    field: i32,
}

fn main() {
    let j = serde_json::from_str::<Joeybloggs>("{\"old\":0}").unwrap();
    println!("{}", serde_json::to_string(&j).unwrap());
}

Awesome! @dtolnay thank you for the quick response! seems like your everywhere! ;)

Hi, I am a bit confused about the backwards compatibility. Won't the example above crash if we try to serialize then re-load a freshly created struct?

For example, if I add something like this:

    let fresh_version = Joeybloggs { field: 1234i32 };
    let serialized = serde_json::to_string::<Joeybloggs>(&fresh_version).unwrap();
    println!("{:?}", serialized);

    let reconstructed = serde_json::from_str::<Joeybloggs>(&serialized);
    println!("Reconstructed: {:?}", reconstructed);

I can serialize it just fine, but then if I attempt to reconstruct the struct I just created, I get this error:

Reconstructed: Err(Error("missing field `old`", line: 1, column: 12))

If I just pass 'old' to both 'deserialize' and 'serialize', then the code works but it also means that any new serialized structs will just use the old-school API forever.

Solution: I found out that I can instead use alias to get what I want. Here is the full code which is backwards compatible with structs containing 'old', but uses 'field' for current and future cases.

extern crate serde;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Debug)]
struct Joeybloggs {
    #[serde(alias = "old")]
    field: i32,
}

fn main() {
    let j = serde_json::from_str::<Joeybloggs>("{\"old\":0}").unwrap();
    println!("{}", serde_json::to_string(&j).unwrap());

    let fresh_version = Joeybloggs { field: 1234i32 };
    let serialized = serde_json::to_string::<Joeybloggs>(&fresh_version).unwrap();
    println!("{:?}", serialized);

    let reconstructed = serde_json::from_str::<Joeybloggs>(&serialized);
    println!("Reconstructed: {:?}", reconstructed);
}

#[cfg(test)]
mod tests {}