Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow integers (or custom types) to be used as names/keys #1773

Open
mjbshaw opened this issue Apr 9, 2020 · 1 comment · May be fixed by #2209
Open

Allow integers (or custom types) to be used as names/keys #1773

mjbshaw opened this issue Apr 9, 2020 · 1 comment · May be fixed by #2209

Comments

@mjbshaw
Copy link

mjbshaw commented Apr 9, 2020

This is similar to #745 ("Allow integer tags for internally tagged enums") but this issue focuses on types and struct fields.

When working with structs and fields, the Serializer and Deserializer traits take name: &'static str parameters, and other traits like SerializeStruct take key: &'static str parameters. These parameters describe the type/field.

But not all formats use textual keys. Some, for example, uses integers for their keys. I'd like to be able to use #[serde(rename = 42)] (or #[serde(rename = SOME_CONST)] if that's not too much), which would allow me to provide serde with the integer key for that particular type or field. But doing this is problematic for two reasons:

  1. It requires Serializer/Deserializer/etc. to use a different type in their methods for the name or key parameters.
  2. It doesn't play well when trying to (de)serialize the same struct to/from a human readable format (e.g., JSON).

I think this could be worked around by using the is_human_readable() function and allowing users to provide both a human-readable and machine-readable name to Serde. For example:

#[derive(Serialize, Deserialize)]
#[serde(rename = "ThisIsANameForHumans")]
#[serde(machine_name = 0x10)]
struct Foo {
    #[serde(rename = "Bar")]
    #[serde(machine_name = 0x21)]
    bar: u32,
    #[serde(rename = "Baz")]
    #[serde(machine_name = 0x8014)]
    baz: f32,
}

Then the #[derive] macros could be changed like so:

impl Serialize for Foo {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        // The check for is_human_readable() is only added if machine_name is used.
        if serializer.is_human_readable() {
            let mut foo = serializer.serialize_struct("ThisIsANameForHumans", 2)?;
            foo.serialize_field("Bar", &self.bar)?;
            foo.serialize_field("Baz", &self.baz)?;
            foo.end()
        } else {
            let mut foo = serializer.serialize_machine_struct(0x10, 2)?;
            foo.serialize_field(0x21, &self.bar)?;
            foo.serialize_field(0x8014, &self.baz)?;
            foo.end()
        }
    }
}

Open questions:

  • If only some of the fields have machine_name specified, how should the be handled? Fallback to using human names? Or fallback to using the field ordinal number? Or fail to compile?
  • Not all formats use integers as keys. Should the machine_name instead take a string of bytes (i.e. a byte literal that can have non-UTF-8 contents)?

I'd be willing to take a shot at implementing this if the serde maintainers are interested.

@noonien
Copy link

noonien commented Nov 22, 2020

This would be great!

I'd like to use MessagePack to encode a struct with protobuf-like tags, where each field has a number instead of a name.

Anything I can do to help with implementing this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging a pull request may close this issue.

2 participants