avro-kotlin / avro4k

Avro format support for Kotlin
Apache License 2.0
198 stars 37 forks source link

Support for abstract / polymorphic classes #108

Closed williamboxhall closed 3 years ago

williamboxhall commented 3 years ago

According to the kotlinx serialization docs it should be possible to register the subtypes of an abstract class as part of the serializerModule. However, it doesn't seem to work:

@file:UseContextualSerialization
...
    val module = SerializersModule {
        polymorphic(SkillsCoachEvent::class) {
            subclass(AccountSettingsEvent::class)
        }
    }
    val schema = Avro(configuration = AvroConfiguration(SnakeCaseNamingStrategy), serializersModule = module).schema(Event.serializer())

...

@Serializable
data class Event(
    val eventId: UUID,
    val createdAt: DateTime,
    val aggregateId: UUID,
    val sequence: Int,
    val aggregateType: String,
    val eventType: String,
    val metadata: ManagerLabMetadata,
    @Polymorphic @Contextual val body: SkillsCoachEvent
)

...

@Serializable
@Polymorphic
abstract class SkillsCoachEvent : DomainEvent

As you can see, I also naively tried adding both @Polymorphic and @Contextual and @file:UseContextualSerialization clutching at straws that they'D offer the hints necessary for Avro4K to treat this as SerialKind.CONTEXTUAL instead of SerialKind.OPEN, to no available. I suspect that Avro4K might need to introduce a new SerialKind.POLYMORPHIC or something like that, to match what's in kotlinx serialization.

Exception in thread "main" kotlinx.serialization.SerializationException: Unsupported type kotlinx.serialization.Polymorphic<SkillsCoachEvent> of OPEN
    at com.github.avrokotlin.avro4k.schema.SchemaForKt.schemaFor(SchemaFor.kt:206)
    at com.github.avrokotlin.avro4k.schema.SchemaForKt.schemaFor(SchemaFor.kt:187)
    at com.github.avrokotlin.avro4k.schema.ClassSchemaFor.buildField(ClassSchemaFor.kt:78)
    at com.github.avrokotlin.avro4k.schema.ClassSchemaFor.dataClassSchema(ClassSchemaFor.kt:63)
    at com.github.avrokotlin.avro4k.schema.ClassSchemaFor.schema(ClassSchemaFor.kt:43)
    at com.github.avrokotlin.avro4k.Avro.schema(Avro.kt:264)
    at com.github.avrokotlin.avro4k.Avro.schema(Avro.kt:269)
    at events.AvroSchemaGeneratorKt.main(AvroSchemaGenerator.kt:37)
williamboxhall commented 3 years ago

I tried to work around this with @org.apache.avro.reflect.Union but it didn't help

williamboxhall commented 3 years ago

As mentioned in https://github.com/avro-kotlin/avro4k/issues/101#issuecomment-890573467 @thake says OPEN polymorphic references aren't supported in Avro4k. @thake are there any plans to change this? Can you think of a workaround for this short of making this sealed (sadly not a great option in our codebase)

thake commented 3 years ago

It is planned to support this feature. It should not be too hard to implement. @williamboxhall If you want to give it a try, I will be very happy to review a PR for this feature.

williamboxhall commented 3 years ago

Thanks @thake that's a fair call. I'm not sure if I'll be able to absorb getting up to speed in avro4ks internals and then doing this PR while dealing with our internal deadlines, but I will definitely try :).

In the meantime, our workaround is probably to just use properly sealed classes, which causes some other significant compromises but gives us a path forward.

williamboxhall commented 3 years ago

I've taken a stab at making a PR for this: https://github.com/avro-kotlin/avro4k/pull/109

thake commented 3 years ago

Implemented with #110