Kotlin Serialization事始め

# はじめに

僕が神と崇めているJake Wharton氏([JakeWharton](https://github.com/JakeWharton))のGitHubを眺めていたら、 **Kotlin Serialization** なるものを使っていることが分かりました。神が使っているものだから、きっと我々下々の民に素晴らしい恩恵を与えるものに違いない:relaxed:そう思い使い方を調べてみました。

# Kotlin Serializationとは

>Kotlin serialization consists of a compiler plugin, which automatically produces visitor code for classes, and runtime library, which uses generated code to serialize objects without reflection.

[公式リポジトリ](https://github.com/Kotlin/kotlinx.serialization)の説明によると、『Kotlin Serializationはクラスのビジターコードを自動的に生成するコンパイラプラグインと、生成されたコードを使用してリフレクションなしでオブジェクトをシリアル化するランタイムライブラリで構成されている』そうです。はぁ:confused:

さらに、Kotlin Serializationは以下の機能を備えているそうです。

* `@Serializable`及び標準コレクションとしてマークされているKotlinクラスをサポート
* JSON、CBOR及びProtobuf形式をサポート
* 同様のコードがJVM/JS/Nativeで機能するマルチプラットフォーム

要するに、Web APIなどのレスポンスをサービスで利用できる形式のオブジェクトに変換する、パーサのようなものかなと。JSONのパーサだと、GsonやMoshiなどが有名ですね。また、これらのパーサと比べて、リフレクションを使用していないため、動作が早いんだとか。ほほう。

# 導入

コンパイラプラグインをプロジェクトルートに追加します。

```gradle:build.gradle
buildscript {
    ext.kotlin_version = '1.3.30'

    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
    }
}
```

なお、Kotlinのバージョンは`1.3.30`以上が必要とのことです。続いて、モジュールに依存関係とプラグインに関する追記をします。

```gradle:build.gradle
apply plugin: 'kotlinx-serialization'

repositories {
    maven { url "https://kotlin.bintray.com/kotlinx" }
}

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.11.0'
}
```

全ての追記が終わったら`Sync Project`を実行し、エラーが出ていなければOKです。

# 使い方

## モデルの作成

以下のようなJSON形式の場合

```json:repo.json
{
	"name": "dagger",
	"description": "A fast dependency injector for Android and Java. http://google.github.io/dagger",
	"language": "Java",
	"html_url": "https://github.com/google/dagger",
	"watchers_count": 563,
	"stargazers_count": 13294,
	"forks_count": 2672,
	"owner": {
		"login": "google",
		"avatar_url": "https://avatars1.githubusercontent.com/u/1342004?v=4"
	}
}
```

こんなモデルを作ります。

```kotlin:Repo.kt
@Serializable
data class Repo(
    val name: String,
    val description: String? = null,
    val language: String? = null,
    @SerialName("html_url") val url: String,
    @SerialName("watchers_count") val watchers: Int,
    @SerialName("stargazers_count") val stars: Int,
    @SerialName("forks_count") val forks: Int,
    val owner: Owner
)
```

```kotlin:Owner.kt
@Serializable
data class Owner(
    val login: String,
    @SerialName("avatar_url") val avatar: String
)
```

`@Serializable`アノテーションを付けたKotlinクラスがシリアライズの対象になります。`@SerialName`アノテーションはJSONのキー名と違ったパラメータを紐づける場合に使います。その他のアノテーションは[公式のドキュメント](https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/examples.md#annotations)を参考にしてください。

## シリアライズ&パース(追記)

重要な部分が抜けていたので追記します:sweat:

大事なことは全て公式の[README](https://github.com/Kotlin/kotlinx.serialization/blob/master/README.md)に書かれていますが…以下は先ほどの`Repo`モデルをシリアライズ&パースする一例です。

```kotlin
// ダミーデータ
val repo = Repo(
    name = "dagger",
    description = "A fast dependency injector for Android and Java. http://google.github.io/dagger",
    language = "Java",
    url = "https://github.com/google/dagger",
    watchers = 563,
    stars = 13294,
    forks = 2672,
    owner = Owner(
        login = "google",
        avatar = "https://avatars1.githubusercontent.com/u/1342004?v=4"
    )
)

// オブジェクトのシリアライズ
val jsonData = Json.stringify(Repo.serializer(), repo)

// リストのシリアライズ
val jsonList = Json.stringify(Repo.serializer().list, listOf(repo))

// 文字列(JSON)のパース
val obj = Json.parse(Repo.serializer(), jsonData)
```

このようにシンプルに扱うことが出来ます。

# Gson/Moshiからの移行

Retrofit+GsonあるいはRetrofit+Moshiなどで、すでにAPIを叩く処理を実装している場合、Kotlin Serializationへの移行はできないのではないか？安心してください。神は我々を見捨てませんでした:pray:

[Kotlin Serialization Converter](https://github.com/JakeWharton/retrofit2-kotlinx-serialization-converter)

Retorfit向けにコンバータを用意してくれていたのです。このコンバータをGsonやMoshi同様に

```kotlin:Api.kt
val contentType = MediaType.get("application/json")
val retrofit = Retrofit.Builder()
    .baseUrl("https://example.com/")
    .addConverterFactory(Json.asConverterFactory(contentType))
    .build()
```

とすれば、Kotlin Serializationが使えます。今後正式にRetrofitのプロジェクトに採用されるかもしれません。

# まとめ

JSON以外にもパースできる形式があったり、独自のシリアライズを定義することもできるのですが、今回はひとまず概要のみで割愛させていただきました。導入に関しては、使用可能なKotlinバージョンの縛りはあるものの、GsonやMoshi同様にアノテーションで簡潔に書くことができました。導入コストも比較的低いのではないでしょうか。また、先述した動作速度の向上に関しても気になるところではあるので、時間がある時に他のパーサと比較してみたいと思います。今回はこのへんで。

# 参考文献

* Kotlin Serialization: https://github.com/Kotlin/kotlinx.serialization