Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Allow encoding null array items or null map values #197

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading