Kotlin’s a great language for JSON

Why you should model your JSON documents with Kotlin

Reddit
LinkedIn

Though it has its wrinkles, I really like JSON. It’s easy to read, pretty fast to parse, and refreshingly simple. Here’s a sample message from GitHub’s exemplar API:

{
  "url": "https://api.github.com/repos/square/okio/issues/156",
  "id": 91393390,
  "number": 156,
  "title": "ByteString CharSequence idea",
  "state": "open",
  "created_at": "2015-06-27T00:49:40.000Z",
  "body": "Let's make CharSequence that's backed by bytes.\n"
}

Kotlin’s concise immutable data classes make it easy to build a basic model for this JSON.

data class Issue(
    val url: String,
    val id: Long,
    val number: Long,
    val title: String,
    val state: String,
    val created_at: String,
    val body: String)

That’s it. No equals(), hashCode(), or toString() boilerplate. We don’t even need a builder! Let’s extend the model to take advantage of Kotlin’s default values and explicit nulls:

data class Issue(
    val url: String,
    val id: Long,
    val number: Long,
    val title: String,
    **val comments: Long = 0L**,
    val created_at: String,
    **val closed_at: String?,**
    **val body: String = ""**)

Default values fill in the gaps when decoding JSON from the network. I like that I can leave them out when creating sample data in my test cases. Explicit nullable types prevent data problems.

Today we’re releasing Moshi 1.5 with powerful Kotlin support via the moshi-kotlin module. Moshi’s type adapters and annotations bind JSON to an idiomatic data model.

data class Issue(
    val url: String,
    val id: Long,
    val number: Long,
    val title: String,
    **val state: IssueState**,
    val comments: Long = 0L,
    **@Json(name = "created_at") val createdAt: Date**,
    **@Json(name = "closed_at") val closedAt: Date?**,
    val body: String = "")

This class uses proper types instead of strings for the issue’s state and timestamps. The @Json annotation maps snake_case names in JSON to camelCase property names in Kotlin.

To set this up I need a Moshi.Builder and a JsonAdapter. I can use Kotlin’s raw strings to embed a sample message right in the code.

val issueJson = """
{
  "url": "[https://api.github.com/repos/square/okio/issues/156](https://api.github.com/repos/square/okio/issues/156)",
  "id": 91393390,
  "number": 156,
  "title": "ByteString CharSequence idea",
  "state": "open",
  "created_at": "2015-06-27T00:49:40.000Z"
}
"""

val moshi = Moshi.Builder()
    .add(KotlinJsonAdapterFactory())
    .add(Date::class.java, Rfc3339DateJsonAdapter().nullSafe())
    .build()

val issueAdapter = moshi.adapter(Issue::class.java)
val issue = issueAdapter.fromJson(issueJson)

If you’re using JSON, Moshi and Kotlin help you to build better models with less code. Note that moshi-kotlin uses kotlin-reflect for property binding. That dependency is large by Android standards (1.7 MiB / 11,500 methods). We’re thinking of creative ways to address that!

This post is part of Square’s “Square Open Source ♥s Kotlin” series.