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:
- What I need to use for serializer’s descriptor property? What I did works, but I’m not sure if it’s correctly implemented.
- 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.