Closed codehearts closed 3 years ago
Hi, thank you for the detailed report and fix suggestions.
I like the easy fix with #[serde(untagged)]
but there is the issue you observed due to ordering of untagged enums:
Serde will try to match the data against each variant in order and the first one that deserializes successfully is the one returned.
The painful fix seems indeed painful for things like records.
I'll try to think about what's best, but I feel it will be the painful one...
Another possibility is to handle it directly in avro-rs
as follows:
But that implies to hardcode variant names in EnumUnionDeserializer::variant_seed
which would be fined if rsgen-arvro
were part of avro-rs
but is problematic for now as those hardcoded names could be different (they depend on the deserialization target).
That's my thought exactly, the painful fix is probably best but it's unfortunate that there's no good way to share the visitor code. If it's alright with you, I can get started on a PR to implement this and gate it behind a feature flag if you don't think it should be there by default. I've been trying to find a cleaner way to implement it, but I haven't been able to figure anything else out.
Sure I'd be glad to get some help so a PR is welcome. I don't think a feature flag is needed, it could be gated by a GeneratorBuilder
flag like GeneratorBuilder::use_variant_access which is used in the Templater
to customize templating.
I'll keep looking at serde-based solutions, maybe there is some serde magic that we could leverage here (to avoid to have some dedicated templating logic for this specific visitor).
Implemented in 0.9.6
for types supported by avro-rs
's unions.
I'm trying to deserialize an
avro_rs::types::Value
to a struct generated by rsgen_avro, but the deserialization fails on theOption<UnionIntLong>
field with avro_rs saying "not an enum".What's happening?
The issue seems to be avro_rs using a custom deserializer, which the serde-generated implementation for
UnionIntLong
callsdeserialize_enum
on. Since the Avro-encoded union doesn't contain an enum, the deserialization fails.I've set up a repo with a full reproduction of the issue and 2 proposed solutions. Both solutions might need to be behind a feature flag, since they may be breaking changes.
An easy fix
#[serde(untagged)]
long
types get deserialized to theUnionIntLong::Int
variantIn my case, the caveat is fine because I'm casting both variants to a single Rust type in my codebase anyway.
A painful fix
#[derive(serde::Deserialize)]
annotation from the union typeserde::de::Visitor
for the union typeserde::Deserializer
for the union type using the custom visitorThis deserializes Avro types to their correct variants, so a
long
would deserialize asUnionIntLong::Long
. The downside is how much additional code and complexity this adds.There might be a better way of handling this, and maybe avro_rs is the right crate to implement a fix for this in, but I thought I'd open an issue here and see what the thoughts are.