Skip to content

Commit

Permalink
Merge pull request #197 from Chuckame/fix/allow-nulls-collection-items
Browse files Browse the repository at this point in the history
fix: Allow encoding null array items or null map values
  • Loading branch information
Chuckame committed Apr 26, 2024
2 parents 1723ebc + 35609a1 commit 1e6d6b3
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ class ListDecoder(
return array[index]
}

override fun decodeNotNullMark() = array[index] != null
override fun fieldSchema(): Schema = schema.elementType

override fun decodeEnum(enumDescriptor: SerialDescriptor): Int {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ class MapDecoder(

private fun value(): Any? = entries[index / 2].second

override fun decodeNotNullMark() : Boolean {
val entry = entries[index / 2]
return when {
index % 2 == 0 -> entry.first
else -> entry.second
} != null
}

override fun decodeFloat(): Float {
return when (val v = value()) {
is Float -> v
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ class ListEncoder(private val schema: Schema,
list.add(value)
}

override fun encodeNull() {
list.add(null)
}

override fun encodeString(value: String) {
list.add(StringToAvroValue.toValue(schema, value))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class MapEncoder(schema: Schema,
CompositeEncoder,
StructureEncoder {

private val map = mutableMapOf<Utf8, Any>()
private val map = mutableMapOf<Utf8, Any?>()
private var key: Utf8? = null
private val valueSchema = schema.valueType

Expand All @@ -39,6 +39,14 @@ class MapEncoder(schema: Schema,
}
}

override fun encodeNull() {
val k = key
if (k == null) throw SerializationException("Expected key but received null value") else {
map[k] = null
key = null
}
}

override fun endStructure(descriptor: SerialDescriptor) {
callback(map.toMap())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,51 +7,58 @@ import io.kotest.core.spec.style.wordSpec
import kotlinx.serialization.Serializable

class ArrayEncoderTest : WordSpec({
includeForEveryEncoder { arrayEncodingTests(it) }
includeForEveryEncoder { arrayEncodingTests(it) }
})

@Suppress("ArrayInDataClass")
fun arrayEncodingTests(encoderToTest: EnDecoder): TestFactory {
return wordSpec {
"en-/decoder" should {
"generate GenericData.Array for an Array<Boolean>" {
@Serializable
data class ArrayBooleanTest(val a: Array<Boolean>)

val value = ArrayBooleanTest(arrayOf(true, false, true))
encoderToTest.testEncodeDecode(value, record(value.a.asList()))
}
"support GenericData.Array for an Array<Boolean> with other fields" {
@Serializable
data class ArrayBooleanWithOthersTest(val a: String, val b: Array<Boolean>, val c: Long)

val value = ArrayBooleanWithOthersTest("foo", arrayOf(true, false, true), 123L)
encoderToTest.testEncodeDecode(
value, record(
"foo",
listOf(true, false, true),
123L
)
)
}

"generate GenericData.Array for a List<String>" {
@Serializable
data class ListStringTest(val a: List<String>)
encoderToTest.testEncodeDecode(
ListStringTest(listOf("we23", "54z")), record(
listOf("we23", "54z")
)
)
}
"generate GenericData.Array for a Set<Long>" {
@Serializable
data class SetLongTest(val a: Set<Long>)

val value = SetLongTest(setOf(123L, 643L, 912L))
val record = record(listOf(123L, 643L, 912))
encoderToTest.testEncodeDecode(value, record)
}
}
}
return wordSpec {
"en-/decoder" should {
"generate GenericData.Array for an Array<Boolean>" {
@Serializable
data class ArrayBooleanTest(val a: Array<Boolean>)

val value = ArrayBooleanTest(arrayOf(true, false, true))
encoderToTest.testEncodeDecode(value, record(value.a.asList()))
}
"generate GenericData.Array for an Array<Boolean?>" {
@Serializable
data class ArrayBooleanTest(val a: Array<Boolean?>)

val value = ArrayBooleanTest(arrayOf(true, null, false))
encoderToTest.testEncodeDecode(value, record(value.a.asList()))
}
"support GenericData.Array for an Array<Boolean> with other fields" {
@Serializable
data class ArrayBooleanWithOthersTest(val a: String, val b: Array<Boolean>, val c: Long)

val value = ArrayBooleanWithOthersTest("foo", arrayOf(true, false, true), 123L)
encoderToTest.testEncodeDecode(
value, record(
"foo",
listOf(true, false, true),
123L
)
)
}

"generate GenericData.Array for a List<String>" {
@Serializable
data class ListStringTest(val a: List<String>)
encoderToTest.testEncodeDecode(
ListStringTest(listOf("we23", "54z")), record(
listOf("we23", "54z")
)
)
}
"generate GenericData.Array for a Set<Long>" {
@Serializable
data class SetLongTest(val a: Set<Long>)

val value = SetLongTest(setOf(123L, 643L, 912L))
val record = record(listOf(123L, 643L, 912))
encoderToTest.testEncodeDecode(value, record)
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@ fun mapEncoderTests(enDecoder: EnDecoder): TestFactory {
)
}

"encode/decode a Map<String, Boolean?>" {
@Serializable
data class StringBooleanTest(val a: Map<String, Boolean?>)
enDecoder.testEncodeDecode(
StringBooleanTest(mapOf("a" to true, "b" to null, "c" to false)),
record(mapOf("a" to true, "b" to null, "c" to false))
)
}

"encode/decode a Map<String, String>" {

@Serializable
Expand Down

0 comments on commit 1e6d6b3

Please sign in to comment.