~tdeo/serde_bare#5: 
Support deserialising externally tagged enums

Hey there,

it doesn't seem to be possible to deserialise an externally tagged enum with serde_bare.

Given the enum:

#[derive(Serialize, Deserialize)]
#[serde(tag = "protocol", content = "data", rename_all = "snake_case")]
pub enum MyProtocol {
    Start { name: Option<String> },
    Confirm { name: String },
}

Serialising this enum works and results in the equivalent of the following encoding schema:

type Payload {
    protocol: string
    data: data
}

But deserialising this enum type does not work and crashes with the following error:

panicked at 'called `Result::unwrap()` on an `Err` value: Message("invalid value: integer `21`, expected variant index 0 <= i < 6")'

Do you know why this is happening?

Status
RESOLVED FIXED
Submitter
~spacekookie
Assigned to
No-one
Submitted
3 years ago
Updated
3 years ago
Labels
No labels applied.

~tdeo 3 years ago

Hi, there are some Serde features that only make sense in the context of self-describing serialization formats like JSON, YAML, and CBOR rather than non-self-describing formats like BARE or Bincode.

In BARE, enum variant names as well as struct fields are completely ignored, so the rename_all in your example has no effect with serde_bare.

Serialising this enum works and results in the equivalent of the following encoding schema:

type Payload {
    protocol: string
    data: data
}

This is actually not what happens, it actually becomes

{
    protocol: string
    data: {
        name: optional<string>
    }
}

when serializing Start and

{
    protocol: string
    data: {
        name: string
    }
}

when serializing Confirm.

While it's technically possible to deserialize such a message properly, I don't believe any BARE implementation can actually represent this as a native type.

When the derived Deserialize implementation goes to deserialize it, deserialize_identifier is called.

deserialize_identifier's documentation is:

[...] the name of a struct field or the discriminant of an enum variant.

In formats like JSON, these would both be represented as strings, but in BARE, struct fields are unnecessary (since fields must always be in order), and enum discriminants are simple uints.

It's not possible to know in advance whether we're deserializing a string or a uint, so serde_bare can't support this kind of use.

If you're looking to share Serialize/Deserialize implementations between BARE and other formats, it seems it's likely not possible for cases like this. See https://github.com/bincode-org/bincode/issues/272 for a similar issue with Bincode.

~spacekookie 3 years ago · edit

Hey! Thank you for diving into this issue so deeply. So basically the only consistent way to serialise and deserialise these types is to have each enum variant I want to exist be a concrete type and then do matching on the protocol field in the wrapping message myself. That is a bit unfortunate, but it sounds like a part of the main design of how bare works...

~tdeo 3 years ago

Hi, I'm not sure I understand. You can just remove your #[serde(tag = "protocol", content = "data", rename_all = "snake_case")] and it will be serialized correctly, into a BARE tagged union.

The only case where this should be a problem is when trying to use the same Serde implementations for multiple data formats, and you're using overrides to fit with an existing convention.

~spacekookie REPORTED FIXED 3 years ago · edit

Hi, I'm not sure I understand. You can just remove your #[serde(tag = "protocol", content = "data", rename_all = "snake_case")] and it will be serialized correctly, into a BARE tagged union.

Yes, I am aware. But this is not possible in the constraints that I'm working in. Basically we need externally tagged type information for cross-language compatibility. I was hoping that I could use this serde feature but I understand why I can't. I ended up writing my own abstraction that wraps any payload into a carrier payload struct that has "protocol" and "data" fields (where there's no ambiguity about what is being parsed).

Register here or Log in to comment, or comment via email.