import io.ktor.client.HttpClient import io.ktor.client.call.body import io.ktor.client.engine.okhttp.OkHttp import io.ktor.client.plugins.contentnegotiation.ContentNegotiation import io.ktor.client.request.get import io.ktor.serialization.kotlinx.json.json import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable import kotlinx.serialization.descriptors.SerialDescriptor import kotlinx.serialization.descriptors.buildClassSerialDescriptor import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonDecoder import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonObject import kotlinx.serialization.modules.SerializersModule import kotlinx.serialization.modules.contextual @Serializable data class ErrorResponse( val errorCode: String, val message: String ) sealed class ApiResponse { data class Success(val value: S) : ApiResponse() data class Failure(val error: ErrorResponse) : ApiResponse() } class ApiResponseSerializer( private val successSerializer: KSerializer ) : KSerializer> { override val descriptor: SerialDescriptor = buildClassSerialDescriptor("ApiResponse") override fun deserialize(decoder: Decoder): ApiResponse { require(decoder is JsonDecoder) { "ApiResponseSerializer can be used only with JSON format" } val element: JsonElement = decoder.decodeJsonElement() if (isError(element)) { val err = decoder.json.decodeFromJsonElement(ErrorResponse.serializer(), element) return ApiResponse.Failure(err) } val value = decoder.json.decodeFromJsonElement(successSerializer, element) return ApiResponse.Success(value) } override fun serialize(encoder: Encoder, value: ApiResponse) { TODO() } private fun isError(element: JsonElement): Boolean { val obj = element as? JsonObject ?: return false return "errorCode" in obj } } @Serializable data class ConsentResponse(val hasGivenConsent: Boolean) suspend fun main() { val client = HttpClient(OkHttp) { install(ContentNegotiation) { json( Json { ignoreUnknownKeys = true serializersModule = SerializersModule { contextual(ApiResponseSerializer(ConsentResponse.serializer())) } } ) } } val response = client.get("http://0.0.0.0:8090/success") val body = response.body>() when (body) { is ApiResponse.Success -> { println(body.value) } is ApiResponse.Failure -> { println(body.error.errorCode) } } client.close() }