【Kotlin】kotlinx.serializationを使ってjson parseをやってみる

# kotlinx.serializationとは
>Kotlin serialization consists of a compiler plugin, that generates visitor code for serializable classes, runtime library with core serialization API and JSON format, and support libraries with ProtoBuf, CBOR and properties formats.

Serialize可能なクラスのvisitor codeを生成するコンパイラプラグインとcore serialization APIおよびJSON形式のランタイムライブラリで構成している。

つまり、Kotlinで作られたSerializerということですね。

また、そのほかの特徴としては
>・Supports Kotlin classes marked as @Serializable and standard collections.
・Provides JSON, CBOR, and Protobuf formats.
・Complete multiplatform support: JVM, JS and Native.

など挙げられており、KotlinでのMultiplatform開発では特に活躍しそうです。
また、二つ目にあるformat以外にもv0.2.0から [HOCON](https://github.com/Kotlin/kotlinx.serialization/blob/master/formats/README.md#hocon-config-parser) なども追加されたようです。


その他対応format一覧
https://github.com/Kotlin/kotlinx.serialization/blob/master/formats/README.md

Androidでは似たようなJson parserにGson、Jackson、Moshi、kotshiなどがあります。

# v0.2.0

2020/03/10現在のlatest versionは[v0.2.0](https://github.com/Kotlin/kotlinx.serialization/releases/tag/v0.20.0)となります。
そのため導入した際には都度更新差分を早めにキャッチアップすることをおすすめします。


# 導入方法

Androidを例にして導入していきます。

### gradle
まずはproject rootのbuild.gradleにコンパイラプラグインを追加します。

```gradle:build.gradle

buildscript {
    ext.kotlin_version = "1.3.70" // 1

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

1. 最低でもv1.3.30以上には指定する必要があります。

次に利用するmoduleのbuild.gradleにランタイムライブラリを追記します。

```gradle:build.gradle
repositories {
    jcenter()
}

dependencies {
    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" // or "kotlin-stdlib-jdk8"
    implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.20.0"
}

```

また、Kotlin DSLで記述すると下記になります。

```gradle:build.gradle.ktx
plugins {
    kotlin("multiplatform") // or kotlin("jvm") or any other kotlin plugin
    kotlin("plugin.serialization") version "1.3.70"
}

```

```gradle:build.gradle.ktx
buildscript {
    repositories {
    // artifacts are published to JCenter
    jcenter()
}

dependencies {
    implementation(kotlin("stdlib", KotlinCompilerVersion.VERSION)) // or "stdlib-jdk8"
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.20.0") // JVM dependency
}

```

### proguard
proguardを利用している場合はrules追加が必要です。

```text:proguard-rules.pro
-keepattributes *Annotation*, InnerClasses
-dontnote kotlinx.serialization.SerializationKt
-keep,includedescriptorclasses class com.yourcompany.yourpackage.**$$serializer { *; } # <-- change package name to your app's
-keepclassmembers class com.yourcompany.yourpackage.** { # <-- change package name to your app's
    *** Companion;
}
-keepclasseswithmembers class com.yourcompany.yourpackage.** { # <-- change package name to your app's
    kotlinx.serialization.KSerializer serializer(...);
}
```

これで導入は終わりです。

#使用例

APIからのレスポンスがjsonできた場合のparseを例にご紹介します。

### data class
まずは、Data classを作成します。

```kotlin:Data.kt

import kotlinx.serialization.*
import kotlinx.serialization.json.*

@Serializable // 1
data class Data(
  val id: Int,
  @SerialName("user_name") // 2
  val name: String = "hoge",
  @SerialName("mail_address")
  val mailAddress: String? = "" // 3
)
```

1. `@Serializable` AnnotationをつけてSerializeの対象にします。
2. `@SerialName` Annotationをつけると、parseした際のkey名を指定することができるので変数名を変えることができます。
3. parseした際にそのdataがない可能性がある場合はあらかじめnullableにしておきます。（対応方法は後述）

その他にも`@Required`など様々なAnnotationがあるので気になる方は[コチラ](https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/examples.md#annotations)をご覧ください。

### parse
上記のDataクラスを基にparseを処理を書きます。

```kotlin
// sample data
val json = """{"id":0,"user_name":"hoge","mail_address":"aaa@gmail.com"}"""

val obj = Json(JsonConfiguration.Stable).parse(Data.serializer(), json) // 1
```

1. `Json(JsonConfiguration.Stable`はjsonをparseする上での設定になります。 `JsonConfiguration.Stable`はデフォルト設定なので省略も可能です。

### その他のJsonConfiguration

しかし、jsonデータの中にある`mail_address`が返ってこないときに上記のparseをしてしまうと、parse errorが起きてしまいます。
そこで、nullableなvalueがある時にはその設定をしないといけません。


```kotlin
// sample data
val json = """{"id":0,"user_name":"hoge"}"""

val parsedJson = Json(
    JsonConfiguration(
       isLenient = true, // 1
       ignoreUnknownKeys = true,
       serializeSpecialFloatingPointValues = true
    )
).parse(Data.serializer(), json)
```
これで下記をnullableなvalueを扱っても問題なくparseをしてくれます。
1. それぞれのConfigの意味は[コチラ](https://github.com/Kotlin/kotlinx.serialization/blob/ffe216f0293231b09eea24a10aa4bc26ff6d5b90/runtime/commonMain/src/kotlinx/serialization/json/JsonConfiguration.kt#L15-L44)をご覧ください。

# 最後に

v0.14.0ではnullableなvalueがある場合は下記の指定をすればよかったので楽でした。

```kotlin
// sample data
val json = """{"id":0,"name":"hoge"}"""

val obj = Json.nonstrict.parse(Data.serializer(), json)
```
v0.2.0でも`@UnstableDefault`がかなりの量があるので更新は随時追っていきましょう！