sksamuel / avro4s

Avro schema generation and serialization / deserialization for Scala
Apache License 2.0
714 stars 236 forks source link

Decoder fails to decode Unions #808

Closed vkorchik closed 7 months ago

vkorchik commented 7 months ago

Example that fails (using Fruit sealed trait from SealedTraitDecoderTest):

  test("support round-trip for sealed traits of case classes") {
    @AvroNamespace("market")
    case class Garden(fruit1: Fruit, fruit2: Fruit)

    val schema = AvroSchema[Garden]

    val garden = Garden(Apple(2.45), Orange("blue"))

    val record = Encoder[Garden].encode(schema)(garden)
    val gardenAgain = Decoder[Garden].decode(schema)(record)

    gardenAgain shouldBe garden
  }

Error is

com.sksamuel.avro4s.Avro4sConfigurationException: Cannot find subschema for type [com.sksamuel.avro4s.record.decoder.Apple] in [{"type":"record","name":"Apple","namespace":"market","fields":[{"name":"weight","type":"double"}]}, {"type":"record","name":"Orange","namespace":"market","fields":[{"name":"color","type":"string"}]}]

Digging a bit deeper into the encoder/decoder machinery, I found out that typeutils.Names are created differently for TypeUnions.encoder and TypeUnions.decoder. Former does utilize proper namespace (from Annotations), latter - does not.

What I have done is basically passed Annotations to decoder via main constructor, the same as it is done for encoder. And it solved the issue.

// avro4s-core/src/main/scala/com/sksamuel/avro4s/decoders/unions.scala

      val decodersByName = ctx.subtypes.map { st =>
        val annos: Annotations = Annotations(st.annotations) // <-- this was added
        val names = Names(st.typeInfo, annos) // <-- and passed here
        val subschema = SchemaHelper.extractTraitSubschema(names.fullName, schema)
        names.fullName -> st.typeclass.decode(subschema)
      }...

And now the question: is it a bug or is it me who cannot use library properly? I am very novice in avro4s, so I could be wrong in my discoveries ;) Oh, and it is the latest version: 5.0.7 (i.e. for Scala 3)