Ktor Version and Engine Used (client or server and name)
kotlin: 1.3.61
kotlinx-serialization-runtime: 0.14.0
ktor-client: 1.3.0-rc
Describe the bug
Setup HttpClient with JsonFeature and serializer KotlinxSerializer.
When running following request:
import kotlinx.serialization.json.json
httpClient.post<String>("http://example.com") {
contentType(ContentType.Application.Json)
body = json {
"key1" to "abc"
"map" to json {
"key2" to "abc"
}
}
}
got following exception:
java.lang.ClassCastException: kotlinx.serialization.json.JsonObject cannot be cast to kotlinx.serialization.json.JsonLiteral
at kotlinx.serialization.json.JsonLiteralSerializer.serialize(JsonElementSerializer.kt:103)
at kotlinx.serialization.json.internal.StreamingJsonOutput.encodeSerializableValue(StreamingJsonOutput.kt:228)
at kotlinx.serialization.ElementValueEncoder.encodeSerializableElement(ElementWise.kt:72)
at kotlinx.serialization.internal.MapLikeSerializer.serialize(CollectionSerializers.kt:134)
at kotlinx.serialization.json.internal.StreamingJsonOutput.encodeSerializableValue(StreamingJsonOutput.kt:228)
at kotlinx.serialization.CoreKt.encode(Core.kt:74)
at kotlinx.serialization.json.Json.stringify(Json.kt:95)
at io.ktor.client.features.json.serializer.KotlinxSerializer.write(KotlinxSerializer.kt:63)
at io.ktor.client.features.json.JsonFeature$Feature$install$1.invokeSuspend(JsonFeature.kt:101)
at io.ktor.client.features.json.JsonFeature$Feature$install$1.invoke(JsonFeature.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:273)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:141)
at io.ktor.util.pipeline.SuspendFunctionGun.proceedWith(PipelineContext.kt:151)
at io.ktor.client.features.HttpCallValidator$Companion$install$1.invokeSuspend(HttpCallValidator.kt:87)
at io.ktor.client.features.HttpCallValidator$Companion$install$1.invoke(HttpCallValidator.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:273)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:141)
at io.ktor.util.pipeline.SuspendFunctionGun.execute(PipelineContext.kt:161)
at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:27)
at io.ktor.client.HttpClient.execute(HttpClient.kt:155)
at io.ktor.client.statement.HttpStatement.executeUnsafe(HttpStatement.kt:99)
Moreover if we try to use map with with different value types this also leads to exception:
httpClient.post<String>("http://example.com") {
contentType(ContentType.Application.Json)
body = mapOf(
"key1" to "abc",
"map" to mapOf(
"key2" to "abc"
)
)
}
fails with:
java.lang.ClassCastException: java.util.Collections$SingletonMap cannot be cast to java.lang.String
at kotlinx.serialization.internal.StringSerializer.serialize(Primitives.kt:97)
at kotlinx.serialization.json.internal.StreamingJsonOutput.encodeSerializableValue(StreamingJsonOutput.kt:228)
at kotlinx.serialization.ElementValueEncoder.encodeSerializableElement(ElementWise.kt:72)
at kotlinx.serialization.internal.MapLikeSerializer.serialize(CollectionSerializers.kt:134)
at kotlinx.serialization.json.internal.StreamingJsonOutput.encodeSerializableValue(StreamingJsonOutput.kt:228)
at kotlinx.serialization.CoreKt.encode(Core.kt:74)
at kotlinx.serialization.json.Json.stringify(Json.kt:95)
at io.ktor.client.features.json.serializer.KotlinxSerializer.write(KotlinxSerializer.kt:63)
at io.ktor.client.features.json.JsonFeature$Feature$install$1.invokeSuspend(JsonFeature.kt:101)
at io.ktor.client.features.json.JsonFeature$Feature$install$1.invoke(JsonFeature.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:273)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:141)
at io.ktor.util.pipeline.SuspendFunctionGun.proceedWith(PipelineContext.kt:151)
at io.ktor.client.features.HttpCallValidator$Companion$install$1.invokeSuspend(HttpCallValidator.kt:87)
at io.ktor.client.features.HttpCallValidator$Companion$install$1.invoke(HttpCallValidator.kt)
at io.ktor.util.pipeline.SuspendFunctionGun.loop(PipelineContext.kt:273)
at io.ktor.util.pipeline.SuspendFunctionGun.proceed(PipelineContext.kt:141)
at io.ktor.util.pipeline.SuspendFunctionGun.execute(PipelineContext.kt:161)
at io.ktor.util.pipeline.Pipeline.execute(Pipeline.kt:27)
at io.ktor.client.HttpClient.execute(HttpClient.kt:155)
The root cause is incorrect serializer builded for value here: https://github.com/ktorio/ktor/blob/1.3.0-rc/ktor-client/ktor-client-features/ktor-client-json/ktor-client-serialization/common/src/io/ktor/client/features/json/serializer/KotlinxSerializer.kt#L75
1) You should add special case for kotlinx.serialization.json.JsonElement.
2) When you build serializer for Map/List/Array you can not assume that all values are the same type.
This should be fixed in 1.3.0-rc2
Most helpful comment
This should be fixed in 1.3.0-rc2