Skip to content

Commit

Permalink
fixed serialization of open classes in maps and lists
Browse files Browse the repository at this point in the history
  • Loading branch information
thake committed Feb 20, 2023
1 parent 1494d56 commit 57946b5
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ class ListDecoder(
StructureKind.CLASS -> RecordDecoder(descriptor, array[index] as GenericRecord, serializersModule, configuration)
StructureKind.LIST -> ListDecoder(schema.elementType, array[index] as GenericArray<*>, serializersModule, configuration)
StructureKind.MAP -> MapDecoder(descriptor, schema.elementType, array[index] as Map<String, *>, serializersModule, configuration)
PolymorphicKind.SEALED -> UnionDecoder(descriptor,array[index] as GenericRecord, serializersModule, configuration)
PolymorphicKind.SEALED, PolymorphicKind.OPEN -> UnionDecoder(descriptor,array[index] as GenericRecord, serializersModule, configuration)
else -> throw UnsupportedOperationException("Kind ${descriptor.kind} is currently not supported.")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ class MapDecoder(
else -> ListDecoder(schema.valueType, value() as GenericArray<*>, serializersModule, configuration)
}
StructureKind.MAP -> MapDecoder(descriptor, schema.valueType, value() as Map<String, *>, serializersModule, configuration)
PolymorphicKind.SEALED -> UnionDecoder(descriptor, value() as GenericRecord, serializersModule, configuration)
PolymorphicKind.SEALED, PolymorphicKind.OPEN -> UnionDecoder(descriptor, value() as GenericRecord, serializersModule, configuration)
else -> throw UnsupportedOperationException("Kind ${descriptor.kind} is currently not supported.")
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,7 @@ class RecordDecoder(
}

override fun decodeEnum(enumDescriptor: SerialDescriptor): Int {
val symbol = EnumFromAvroValue.fromValue(fieldValue()!!)

val symbol = EnumFromAvroValue.fromValue(fieldValue()!!)
val enumValueByEnumName =
(0 until enumDescriptor.elementsCount).associateBy { enumDescriptor.getElementName(it) }

Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,42 @@
package com.github.avrokotlin.avro4k.io

import com.github.avrokotlin.avro4k.Avro
import com.github.avrokotlin.avro4k.schema.ReferencingPolymorphicRoot
import com.github.avrokotlin.avro4k.schema.UnsealedChildOne
import com.github.avrokotlin.avro4k.schema.UnsealedChildTwo
import com.github.avrokotlin.avro4k.schema.UnsealedPolymorphicRoot
import com.github.avrokotlin.avro4k.schema.*
import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.shouldBe
import kotlinx.serialization.modules.SerializersModule
import kotlinx.serialization.modules.polymorphic
import kotlinx.serialization.modules.subclass
import io.kotest.matchers.types.shouldBeInstanceOf
import org.apache.avro.generic.GenericRecord
import org.apache.avro.util.Utf8

class PolymorphicClassIoTest : StringSpec({
"read / write nested polymorphic class" {
val module = SerializersModule {
polymorphic(UnsealedPolymorphicRoot::class) {
subclass(UnsealedChildOne::class)
subclass(UnsealedChildTwo::class)
}
}
val avro = Avro(serializersModule = module)
writeRead(ReferencingPolymorphicRoot(UnsealedChildOne("one")), ReferencingPolymorphicRoot.serializer(), avro)
writeRead(ReferencingPolymorphicRoot(UnsealedChildOne("one")), ReferencingPolymorphicRoot.serializer(), avro) {
val root = it["root"] as GenericRecord
root.schema shouldBe avro.schema(UnsealedChildOne.serializer())
}
}
"read / write nested polymorphic class" {
val avro = Avro(serializersModule = polymorphicModule)
writeRead(ReferencingPolymorphicRoot(UnsealedChildOne("one")), ReferencingPolymorphicRoot.serializer(), avro)
writeRead(ReferencingPolymorphicRoot(UnsealedChildOne("one")), ReferencingPolymorphicRoot.serializer(), avro) {
val root = it["root"] as GenericRecord
root.schema shouldBe avro.schema(UnsealedChildOne.serializer())
}
}
"read / write nested polymorphic list" {
val avro = Avro(serializersModule = polymorphicModule)
writeRead(PolymorphicRootInList(listOf(UnsealedChildOne("one"))), PolymorphicRootInList.serializer(), avro)
writeRead(PolymorphicRootInList(listOf(UnsealedChildOne("one"))), PolymorphicRootInList.serializer(), avro) {
it["listOfRoot"].shouldBeInstanceOf<List<GenericRecord>>()
val unsealeadChild = (it["listOfRoot"] as List<*>)[0] as GenericRecord
unsealeadChild.schema shouldBe avro.schema(UnsealedChildOne.serializer())
}
}
"read / write nested polymorphic map" {
val avro = Avro(serializersModule = polymorphicModule)
writeRead(PolymorphicRootInMap(mapOf("a" to UnsealedChildOne("one"))), PolymorphicRootInMap.serializer(), avro)
writeRead(
PolymorphicRootInMap(mapOf("a" to UnsealedChildOne("one"))),
PolymorphicRootInMap.serializer(),
avro
) {
it["mapOfRoot"].shouldBeInstanceOf<Map<Utf8, GenericRecord>>()
val unsealeadChild = (it["mapOfRoot"] as Map<*, *>)[Utf8("a")] as GenericRecord
unsealeadChild.schema shouldBe avro.schema(UnsealedChildOne.serializer())
}
}
})
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import com.github.avrokotlin.avro4k.Avro
import io.kotest.core.spec.style.StringSpec
import io.kotest.matchers.shouldBe
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.*
import kotlinx.serialization.modules.SerializersModule
import kotlinx.serialization.modules.polymorphic
import kotlinx.serialization.modules.subclass
import org.apache.avro.Schema

@Serializable
abstract class UnsealedPolymorphicRoot

Expand All @@ -26,6 +26,22 @@ data class ReferencingPolymorphicRoot(
val nullableRoot : UnsealedPolymorphicRoot? = null
)

@Serializable
data class PolymorphicRootInList(
val listOfRoot : List<UnsealedPolymorphicRoot>
)

@Serializable
data class PolymorphicRootInMap(
val mapOfRoot : Map<String, UnsealedPolymorphicRoot>
)
val polymorphicModule = SerializersModule {
polymorphic(UnsealedPolymorphicRoot::class) {
subclass(UnsealedChildOne::class)
subclass(UnsealedChildTwo::class)
}
}

class PolymorphicClassSchemaTest : StringSpec({
"schema for polymorphic hierarchy" {
val module = SerializersModule {
Expand All @@ -39,29 +55,21 @@ class PolymorphicClassSchemaTest : StringSpec({
schema shouldBe expected
}

"supports polymorphic references / nested fields" {
val module = SerializersModule {
polymorphic(UnsealedPolymorphicRoot::class) {
subclass(UnsealedChildOne::class)
subclass(UnsealedChildTwo::class)
}
}
val schema = Avro(serializersModule = module).schema(ReferencingPolymorphicRoot.serializer())
"supports polymorphic references / nested fields" {
val schema = Avro(serializersModule = polymorphicModule).schema(ReferencingPolymorphicRoot.serializer())
val expected = Schema.Parser().parse(javaClass.getResourceAsStream("/polymorphic_reference.json"))
schema shouldBe expected
}
})

object TypeSerializer : JsonTransformingSerializer<UnsealedPolymorphicRoot>(UnsealedPolymorphicRoot.serializer()) {
override fun transformDeserialize(element: JsonElement): JsonElement {
return when (element) {
is JsonPrimitive -> {
buildJsonObject {
put("type", element)
}
}
is JsonObject -> element
else -> throw IllegalAccessException("Unsupported json element type")
}

"Supports polymorphic references in lists" {
val schema = Avro(serializersModule = polymorphicModule).schema(PolymorphicRootInList.serializer())
val expected = Schema.Parser().parse(javaClass.getResourceAsStream("/polymorphic_reference_list.json"))
schema shouldBe expected
}

"Supports polymorphic references in maps" {
val schema = Avro(serializersModule = polymorphicModule).schema(PolymorphicRootInMap.serializer())
val expected = Schema.Parser().parse(javaClass.getResourceAsStream("/polymorphic_reference_map.json"))
schema shouldBe expected
}
}
})
37 changes: 37 additions & 0 deletions src/test/resources/polymorphic_reference_list.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"type": "record",
"name": "PolymorphicRootInList",
"namespace": "com.github.avrokotlin.avro4k.schema",
"fields": [
{
"name": "listOfRoot",
"type": {
"type": "array",
"items": [
{
"type": "record",
"name": "UnsealedChildOne",
"fields": [
{
"name": "one",
"type": "string"
}
]
},
{
"type": "record",
"name": "UnsealedChildTwo",
"fields": [
{
"name": "two",
"type": "string"
}
]
}
]

}
}
]
}

37 changes: 37 additions & 0 deletions src/test/resources/polymorphic_reference_map.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"type": "record",
"name": "PolymorphicRootInMap",
"namespace": "com.github.avrokotlin.avro4k.schema",
"fields": [
{
"name": "mapOfRoot",
"type": {
"type": "map",
"values": [
{
"type": "record",
"name": "UnsealedChildOne",
"fields": [
{
"name": "one",
"type": "string"
}
]
},
{
"type": "record",
"name": "UnsealedChildTwo",
"fields": [
{
"name": "two",
"type": "string"
}
]
}
]

}
}
]
}

0 comments on commit 57946b5

Please sign in to comment.