Kotlix.serialization polymorphism using property presense

I want to implement for my model a polymorphism without a presense of discriminator field. I managed to do this relatively easily in Jackson so now I was trying my luck with kotlix.serialization. I was hoping for some advice how this could be possibly improved:

@Serializable(with = ResponseSerializer::class)
sealed class Response<out T> {
	@Serializable
	data class Success<out T>(val data: T) : Response<T>()
	
	@Serializable
	data class Error(val error: String) : Response<Nothing>()
}

class ResponseSerializer<T>(private val dataSerializer: KSerializer<T>) : KSerializer<Response<T>> {
	override val descriptor get() = dataSerializer.descriptor
	
	override fun serialize(encoder: Encoder, value: Response<T>) {
		when (value) {
			is Response.Success -> Response.Success.serializer(dataSerializer).serialize(encoder, value)
			is Response.Error -> Response.Error.serializer().serialize(encoder, value)
		}
	}
	
	override fun deserialize(decoder: Decoder): Response<T> {
		val json = JsonObject.serializer().deserialize(decoder)
		
		decoder.resetLexerPosition()
		
		if (Response.Success<T>::data.name in json) {
			return Response.Success.serializer(dataSerializer).deserialize(decoder)
		}
		
		if (Response.Error::error.name in json) {
			return Response.Error.serializer().deserialize(decoder)
		}
		
		throw SerializationException("Cannot determine the type to deserialize: $json")
	}
	
	private fun Decoder.resetLexerPosition() {
		val lexerField = javaClass.getField("lexer")
		val lexer = lexerField[this]
		val currentPositionField = lexer.javaClass.superclass.getDeclaredField("currentPosition")
		currentPositionField.isAccessible = true
		currentPositionField.setInt(lexer, 0)
	}
}

fun main() {
	println(Json.decodeFromString<Response<String>>("""{"data":"1234"}"""))
	println(Json.decodeFromString<Response.Success<String>>("""{"data":"1234"}"""))
	println(Json.decodeFromString<Response<List<Int>>>("""{"data":[1,2,3,4]}"""))
	println(Json.encodeToString(Response.Success("1234")))
	
	println(Json.decodeFromString<Response<String>>("""{"error":"BSOD"}"""))
	println(Json.decodeFromString<Response.Error>("""{"error":"BSOD"}"""))
	println(Json.encodeToString(Response.Error("BSOD")))
}

I have 2 main issues here:

  1. What I need to use for serializer’s descriptor property? What I did works, but I’m not sure if it’s correctly implemented.
  2. The greatest problem although for me here is that a once used decoder can’t simply be reused again. What I did here is to hack the lexer via reflection to set it again to the beginning. I would like to know if the is any smarter solution which would give the same result as mine deserializer.

I already found the answer for this issue in documentation. It basically solves my both points. As for the lexer problem it seems like internally it just uses a deserialized json element, so there is no need to touch decoder’s internals. For interested this is how it could be done:

@Serializable(with = ResponseSerializer::class)
sealed class Response<out T> {
	@Serializable
	data class Ok<out T>(val data: T) : Response<T>()
	
	@Serializable
	data class Error(val error: String) : Response<Nothing>()
}

@Suppress("UNCHECKED_CAST")
private class ResponseSerializer<T>(private val dataSerializer: KSerializer<T>) : JsonContentPolymorphicSerializer<Response<T>>(Response::class as KClass<Response<T>>) {
	override fun selectDeserializer(element: JsonElement) = when {
		Response.Ok<T>::data.name in element.jsonObject -> Response.Ok.serializer(dataSerializer)
		Response.Error::error.name in element.jsonObject -> Response.Error.serializer()
		else -> throw SerializationException("Cannot determine the type to deserialize: $element")
	}
}