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?
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 withserde_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
uint
s.It's not possible to know in advance whether we're deserializing a
string
or auint
, soserde_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.
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...
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.
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).