Kotlin JSON serialization crash course

So you want to quickly convert your Data classes to JSON and vice-versa. That should be easy to do. JSON has been around for ages.

In Android (and in the Java world in general) this was traditionally "outsourced" to a dedicated library. In the past, this used to be the Gson library. But lately, especially with Kotlin's null-aware type system, the library fell out of grace.  

There are other modern JSON libraries out there (e.g. Moshi). And Kotlin, since version 1.3, provides its own way way for serializing to and from JSON (and other formats, like protobuf - maybe in another post).

Set up

Kotlin's own way is a compiler plugin with a runtime dependency. You would need to modify your build.gradle a bit, but after that is super easy.

[...]

apply plugin: 'kotlinx-serialization'

[...]

dependencies {
    implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.20.0"

    [...]
}
build.gradle (app)
buildscript {

    [...]
	repositories { jcenter() }

    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
        
        [...]
    }
}
build.gradle (project)

Check out the official doc for the latest runtime version.

Usage

Prepare your data classes

You would need to annotate your data classes with @Serializabe. Note that you can annotate regular classes as well, but there is more limitation on what is supported.

@Serializable
data class MyThing(
     val data: List<Data>,
     val success: Boolean
 ) {
     @Serializable
     data class Data(val balance: String)
}

Notice that if we are serializing a data class that refers to other data classes, the "child" classes should be @Serializable as well.

Serialize/Deserialize

Converting from and to JSON is quite easy.

val myThing: MyThing = Json.parse(MyThing.serializer(), myThingInJsonText)
JSON text -> Data class instance
val myThingInJsonText: String = Json.stringify(MyThing.serializer(), myThing)
Data class instance -> JSON text

The Json static object we are accessing provides the default configuration for the serialization. This configuration, according to the docs, is sensible but it's not guaranteed never to change. So for avoiding unpleasant surprises, you can get your  instance with the stable configuration, for guranating that whatever works today will work tomorrow. Check out the doc for more details.

val json = Json(JsonConfiguration.Stable)
json.parse(.....)
json.stringfy(.....)

Ignore and optional fields

It's common to have some optional fields. Or fields that you just don't want to use  for serialization/deserialization. For optionals, just set a default value and it will be overridden if there's a value in the JSON.

@Serializable
data class MyThing(
     val data: List<Data>,
     val success: Boolean = false
 ) {
     @Serializable
     data class Data(val balance: String)
}
Setting a default value will make the field optional

For ignoring a field, just annotate with @Transient.

@Serializable
data class MyThing(
     val data: List<Data>,
     @Transient
     val success: Boolean
 ) {
     @Serializable
     data class Data(val balance: String)
}
Annotate with Transient to ignore

This post was just a scratch on the surface. You can customize the serialization process whatever your needs are.

On how to convert your JSON to data classes, check out this post. Now enjoy your JSON conversion!