From 8ff945544c8f6bcd01f00451546d9e0f43fd5855 Mon Sep 17 00:00:00 2001 From: sksamuel Date: Sun, 14 Apr 2024 19:58:23 -0500 Subject: [PATCH] Added maps support to encoders --- .../centurion/avro/encoders/Encoder.kt | 1 + .../centurion/avro/encoders/collections.kt | 14 ++++++-- .../encoders/ReflectionRecordEncoderTest.kt | 32 +++++++++++++++++++ changelog.md | 9 ++++++ 4 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 changelog.md diff --git a/centurion-avro/src/main/kotlin/com/sksamuel/centurion/avro/encoders/Encoder.kt b/centurion-avro/src/main/kotlin/com/sksamuel/centurion/avro/encoders/Encoder.kt index 6ba98f2..bcae856 100644 --- a/centurion-avro/src/main/kotlin/com/sksamuel/centurion/avro/encoders/Encoder.kt +++ b/centurion-avro/src/main/kotlin/com/sksamuel/centurion/avro/encoders/Encoder.kt @@ -40,6 +40,7 @@ fun interface Encoder { BigDecimal::class -> BigDecimalStringEncoder Set::class -> GenericArraySetEncoder(encoderFor(type.arguments.first().type!!)) List::class -> GenericArrayListEncoder(encoderFor(type.arguments.first().type!!)) + Map::class -> MapEncoder(encoderFor(type.arguments[1].type!!)) is KClass<*> -> if (classifier.java.isEnum) EnumEncoder>() else error("Unsupported type $type") else -> error("Unsupported type $type") } diff --git a/centurion-avro/src/main/kotlin/com/sksamuel/centurion/avro/encoders/collections.kt b/centurion-avro/src/main/kotlin/com/sksamuel/centurion/avro/encoders/collections.kt index 3c77e82..94d1f46 100644 --- a/centurion-avro/src/main/kotlin/com/sksamuel/centurion/avro/encoders/collections.kt +++ b/centurion-avro/src/main/kotlin/com/sksamuel/centurion/avro/encoders/collections.kt @@ -1,6 +1,7 @@ package com.sksamuel.centurion.avro.encoders import org.apache.avro.Schema +import org.apache.avro.generic.GenericArray import org.apache.avro.generic.GenericData /** @@ -29,9 +30,18 @@ class GenericArrayListEncoder(private val encoder: Encoder) : Encoder(private val encoder: Encoder) : Encoder> { - override fun encode(schema: Schema, value: Set): Any { + override fun encode(schema: Schema, value: Set): GenericArray { require(schema.type == Schema.Type.ARRAY) val elements = value.map { encoder.encode(schema.elementType, it) } - return GenericData.Array(elements.size, schema).also { it.addAll(elements as Collection) } + val array = GenericData.get().newArray(null, elements.size, schema) as GenericArray + array.addAll(elements) + return array + } +} + +class MapEncoder(private val encoder: Encoder) : Encoder> { + override fun encode(schema: Schema, value: Map): Map { + require(schema.type == Schema.Type.MAP) + return value.mapValues { encoder.encode(schema.valueType, it.value) } } } diff --git a/centurion-avro/src/test/kotlin/com/sksamuel/centurion/avro/encoders/ReflectionRecordEncoderTest.kt b/centurion-avro/src/test/kotlin/com/sksamuel/centurion/avro/encoders/ReflectionRecordEncoderTest.kt index 682e2af..c0b755b 100644 --- a/centurion-avro/src/test/kotlin/com/sksamuel/centurion/avro/encoders/ReflectionRecordEncoderTest.kt +++ b/centurion-avro/src/test/kotlin/com/sksamuel/centurion/avro/encoders/ReflectionRecordEncoderTest.kt @@ -91,6 +91,38 @@ class ReflectionRecordEncoderTest : FunSpec({ Foo(listOf(1, 2), listOf(1L, null, 2L), listOf(Wine.Shiraz, Wine.Malbec)) ) shouldBe expected } + + test("maps") { + data class Foo(val map: Map) + + val map = mapOf("a" to 1, "b" to 2) + + val schema = SchemaBuilder.record("Foo").namespace(Foo::class.java.packageName) + .fields() + .name("map").type().map().values(SchemaBuilder.builder().intType()).noDefault() + .endRecord() + + val record = GenericData.Record(schema) + record.put("map", map) + + ReflectionRecordEncoder().encode(schema, Foo(map)) shouldBe record + } + + test("maps of maps") { + data class Foo(val map: Map>) + + val maps = mapOf("a" to mapOf("a" to 1, "b" to 2), "b" to mapOf("a" to 3, "b" to 4)) + + val schema = SchemaBuilder.record("Foo").namespace(Foo::class.java.packageName) + .fields() + .name("map").type().map().values(SchemaBuilder.builder().map().values().intType()).noDefault() + .endRecord() + + val record = GenericData.Record(schema) + record.put("map", maps) + + ReflectionRecordEncoder().encode(schema, Foo(maps)) shouldBe record + } }) enum class Wine { Shiraz, Malbec } diff --git a/changelog.md b/changelog.md new file mode 100644 index 0000000..3cb8587 --- /dev/null +++ b/changelog.md @@ -0,0 +1,9 @@ +## Changelog + +### 1.1.0 + +* Added Map encoding support + +### 1.1.0 + +* Added Avro4s port of Encoders, Decoder, Writers and Readers