Lawrence Gimenez

Kotlin Serialization Shenanigans

It’s been a while since I did some serialization implementation on Android. I forgot how painful this was to implement. This is for my future self, in case I reencounter this stupid shenanigans again.

Gradle Imports

First, import Retrofit, Kotlinx Serialization and Jake Wharton’s Retrofit Kotlinx Serialization Converter.

implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
implementation("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0")

On the build.gradle project level file, don’t forget to also add the dependency inside the plugin section.

plugins {
     id("org.jetbrains.kotlin.plugin.serialization") version "1.9.22" apply false
}

Back to your app level build.gradle file, also add the dependency inside the plugin section.

plugins {
     id("kotlinx-serialization")
}

Retrofit Helper Class

Your Retrofit helper class should be like below. Take note of the import statements.

import com.jakewharton.retrofit2.converter.kotlinx.serialization.asConverterFactory
import kotlinx.serialization.json.Json
import okhttp3.MediaType
import retrofit2.Retrofit

object ApiHelper {

    val baseApi = "https://api.somewebsite.com/"
    private val contentType = MediaType.get("application/json")

    fun getInstance(): Retrofit {
        return Retrofit.Builder()
            .baseUrl(baseApi)
            .addConverterFactory(Json.asConverterFactory(contentType))
            .build()
    }
}

Proguard rules

Don’t forget about the proguard rules.

-if @kotlinx.serialization.Serializable class **
-keepclassmembers class <1> {
   static <1>$Companion Companion;
}

# Keep `serializer()` on companion objects (both default and named) of serializable classes.
-if @kotlinx.serialization.Serializable class ** {
   static **$* *;
}
-keepclassmembers class <2>$<3> {
   kotlinx.serialization.KSerializer serializer(...);
}

# Keep `INSTANCE.serializer()` of serializable objects.
-if @kotlinx.serialization.Serializable class ** {
   public static ** INSTANCE;
}
-keepclassmembers class <1> {
   public static <1> INSTANCE;
   kotlinx.serialization.KSerializer serializer(...);
}

# @Serializable and @Polymorphic are used at runtime for polymorphic serialization.
-keepattributes RuntimeVisibleAnnotations,AnnotationDefault

kotlinx.serialization.MissingFieldException

In your data class, it might seem that adding a nullable type to your variable is enough.

@Serializable
data class User(
    @SerialName("user_id")
    val userId: Int?
)

It’s not. You need to add a default null value.

@Serializable
data class User(
    @SerialName("user_id")
    val userId: Int? = null
)

Shoutout to Jere Schneider and tyczj for the helpful answers that can’t be found anywhere on Android’s documentation.