Search code examples
androidkotlinjson-deserializationmoshikotlinx.serialization

Using kotlin kotlinx serialization with generics


Hi everyone I have the following code that I use to serialize into json strings to store in shared preferences

interface Serializer {
  public fun <T> serialize(data: T, type: Class<T>): String
  public fun <T> deserialize(source: String, type: Class<T>): T?
}

I can later create implementations of this class and serialize my objects using Moshi like the following

class SerializerImpl(
private val moshi: Moshi = Moshi.Builder()
    .build(),
) : Serializer {

override fun <T> deserialize(source: String, type: Class<T>): T? = try {
    moshi.adapter(type).fromJson(source)
} catch (exception: Exception) {
    null
}

override fun <T> serialize(data: T, type: Class<T>): String = 
   moshi.adapter(type).toJson(data)
}

Can you advice how to migrate this implementation to use kotlinx serialization without having to change the signature of the Serializer interface it seems like I must change the type of my Serializer interface to use KSerializer in order for this to work but I would really like to avoid that.


Solution

  • Kotlinx.serialization provides an experimental support for acquiring serializers (KSerializer) dynamically for the provided Type or KType. We can use it to easily implement the Serializer:

    class KotlinxSerializer : Serializer {
        override fun <T> serialize(data: T, type: Class<T>) = Json.encodeToString(type.serializer(), data)
        override fun <T> deserialize(source: String, type: Class<T>) = Json.decodeFromString(type.serializer(), source)
    }
    
    @OptIn(ExperimentalSerializationApi::class)
    @Suppress("UNCHECKED_CAST")
    private fun <T> Class<T>.serializer() = Json.serializersModule.serializer(this) as KSerializer<T>
    

    Usage:

    fun main() {
        val ser = KotlinxSerializer()
        val d1 = MyData("hello")
        val s = ser.serialize(d1, MyData::class.java)
        println(s) // {"foo":"hello"}
        val d2 = ser.deserialize(s, MyData::class.java)
        println(d2) // MyData(foo=hello)
    }
    
    @Serializable
    data class MyData(val foo: String)
    

    Please note we still have to mark all data classes with @Serializable. Kotlinx.serialization can't work without this as this is a compile-time serialization framework.