avro-kotlin / avro4k

Avro format support for Kotlin
Apache License 2.0
188 stars 36 forks source link

Default Value for Enum #89

Closed darmstrong1 closed 3 years ago

darmstrong1 commented 3 years ago

Hi, I would like to use the default feature for enums that is described here : https://avro.apache.org/docs/current/spec.html#Enums.

I tried to use the AvroDefault annotation, giving it the String value of an enum value, but I get a NumberFormatException when I try that. I'd like to use this avro feature so that schemas that use enums can be backward compatible. It seems the only way to achieve this is to give the field a default value that is one of the enum values in case a reader is using an older version that does not know about added values for that enum.

I'm pretty new to avro4k, so maybe avro4k supports this feature in a way I don't know about.

Here is what I tried:

@Serializable
enum class IngredientType { VEGGIE, MEAT, }

@Serializable
data class Ingredient(val name: String, @AvroDefault("MEAT") val type: IngredientType,
    val sugar: Double, val fat: Double,)

@Serializable
data class Pizza(val name: String, val ingredients: List<Ingredient>, val vegetarian: Boolean,
    val kcals: Int,)

fun main() {
  val schema = Avro.default.schema(Pizza.serializer())
  println(schema.toString(true))
}

This is what I get:

Exception in thread "main" java.lang.NumberFormatException: Character M is neither a decimal digit number, decimal point, nor "e" notation exponential mark.
    at java.base/java.math.BigDecimal.<init>(BigDecimal.java:518)
    at java.base/java.math.BigDecimal.<init>(BigDecimal.java:401)
    at java.base/java.math.BigDecimal.<init>(BigDecimal.java:834)
    at com.sksamuel.avro4k.schema.ClassSchemaFor.convertToAvroDefault(ClassSchemaFor.kt:135)
    at com.sksamuel.avro4k.schema.ClassSchemaFor.buildField(ClassSchemaFor.kt:114)
    at com.sksamuel.avro4k.schema.ClassSchemaFor.dataClassSchema(ClassSchemaFor.kt:55)
    at com.sksamuel.avro4k.schema.ClassSchemaFor.schema(ClassSchemaFor.kt:43)
    at com.sksamuel.avro4k.schema.ListSchemaFor.schema(SchemaFor.kt:88)
    at com.sksamuel.avro4k.schema.ClassSchemaFor.buildField(ClassSchemaFor.kt:76)
    at com.sksamuel.avro4k.schema.ClassSchemaFor.dataClassSchema(ClassSchemaFor.kt:55)
    at com.sksamuel.avro4k.schema.ClassSchemaFor.schema(ClassSchemaFor.kt:43)
    at com.sksamuel.avro4k.Avro.schema(Avro.kt:239)
    at co.da.avro.DomainKt.main(Domain.kt:18)
    at co.da.avro.DomainKt.main(Domain.kt)

If this feature is not supported in avro4k, do you plan to support it in the future?

kossi commented 3 years ago

Hi darmstrong1,

I was wondering this myself a bit and opened a small pull request that should resolve this issue. It is a limitation of the current implementation that it tries to resolve enum default values as numbers. But let's if it's okay or the schema structure is a bit different for enum default schemas.

https://github.com/avro-kotlin/avro4k/pull/90

thake commented 3 years ago

Thanks for raising this issue. I think we have 2 problems in the area of defalt values of enums.

The first one can be seen in your code example. Currently there is a bug that does not interprete the default value for enum types correctly.

The second one is the missing implementation of "fallback defaults" for enums that are not known to the reader (schema evolution, see the long thread of https://issues.apache.org/jira/browse/AVRO-1340 for details).

I will open a new issue for the second problem and will continue tracking the first problem in this issue.