Kotlinと相性が良いMoshiのkotlin extensionを使う


# TL;DR
Moshiのkotlin extensionである`KotlinJsonAdapter`を使うと、
kotlinの機能であるnon-nullや、default値を考慮したJSONパースを行ってくれます。

# GsonとKotlinの相性が良くない
私は普段、JSONパーサーにGsonを使っています。
Gsonに強いこだわりはありませんが、Androidでは広く使われており、ネット上に情報が豊富にあるのが使用の主な理由です。
しかし、JSONのパースした結果をKotlinのclassに入れる時に、Gsonだと都合が悪いことがわかりました。

### よくない理由
Kotlinでは、`property`を宣言する時に特に何も指定しなければnon-nullになります。
non-nullな`property`に`null`を渡した場合は例外がthrowされ、classが不正な状態になることを自動で防いでくれます。

```kotlin:JSONを表したDtoクラス
data class Dto(
  val foo: String, // non-null
  val bar: String // non-null
)
```

従って、Gsonを使って、以下のような`bar`というkeyがないJSONをパースした時に、
例外がthrowされるだろうと思っていました。

```text:barが存在しない
{"foo": "value"}
```

しかし、実際は例外はthrowせず、`foo`に`null`という値が入ってしまいます。
結果、classで定義したnon-nullという不変条件が破られてしまいます。

```kotlin
val dto = Gson().fromJson(json, Dto::class.java) //この時点では例外はthrowされない
dto.bar.length // barにはnullが入っており、この時点でNPEがthrowされる
```


この問題を解決するのにMoshiというライブラリを使います。


# Moshiとは
MoshiはSquareが開発したJSONパーサーです。
https://github.com/square/moshi

Square自身もGsonを使っていたそうですが、いくつか不満があり、Moshiの開発に至ったそうです。[参考](https://medium.com/square-corner-blog/moshi-another-json-processor-624f8741f703)
そのような経緯もあり、Gsonよりも使いやすい部分が多いという印象です。

# Moshiのkotlin extensionを使い、Kotlinとの相性を良くする
このMoshiに、Kotlinのclassへのパースをサポートする機能が、今年5月に追加されました。
https://github.com/square/moshi/blob/master/CHANGELOG.md#version-150

>
Kotlin models are now supported via the moshi-kotlin extension.
KotlinJsonAdapterFactory is the best way to use Kotlin with Moshi.
It honors default values and is null-safe. Kotlin users that don't use this factory should write custom adapters for their JSON types. Otherwise Moshi cannot properly initialize delegated properties of the objects it decodes.

### このextensionの機能
この`extension`を使うことで、以下のようなKotlin特有の言語機能をMoshiが補完してくれます

- non-nullの`property`に相当するJSONの値がなかった場合、例外をthrowしてくれる
 - nullableのpropertyは当然例外はthrowせず、`null`が入る
* `property`に相当するJSONの値がなかったが、その`property`にdefault値を指定した場合は、その値が入る
  * ただし、JSONの値が明示的に`null`となっていた場合は、propertyに`null`が入る

より詳細の機能を知りたい場合は、このextensionのテストコードを見ると良いと思います。
このコードから、特定のJSONとkotlinのclassがどう変換され合うのかが分かります。
https://github.com/square/moshi/blob/master/kotlin/src/test/java/com/squareup/moshi/KotlinJsonAdapterTest.kt


### 導入方法
導入方法はextensionをdependencyに追加し、Moshiのインスタンスを作る時に、extensionの本体である`KotlinJsonAdapterFactory`をaddします。

```groovy:app/build.gradle
// dependenciesに以下を追加
def moshiVerion = '1.5.0'
compile "com.squareup.moshi:moshi:$moshiVerion"
compile "com.squareup.moshi:moshi-kotlin:$moshiVerion"
```

```kotlin
val moshi = Moshi.Builder()
    .add(KotlinJsonAdapterFactory()) // kotlin extensionを指定
    .build()
val dto = moshi.adapter(Dto::class.java).fromJson("json")
```

### 導入結果
Gsonを使うことで問題となっていた「non-nullなpropertyにnullが入ってしまうという」という点が、
これを導入すると、「インスタンスにnullが入ることなく、パース時に例外がthrowされる」ようになります。


```kotlin
data class Dto(
  val foo: String, // non-null
  val bar: String // non-null
)

// jsonにbarが存在しない
val json = """
{"foo": "value"}
"""
val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()

// barが存在しないため、パース時にexceptionが発生
val dto = moshi.adapter(Dto::class.java).fromJson(json)
```

# まとめ
Moshiのkotlin extensionを使うことで、Kotlinのnull-safetyのような言語機能を保ったままJSONをパースしてくれます。
今Kotlinを使っていたり、今後Kotlinを追加しようとしている場合は、
Moshiの導入を検討されてはいかがでしょうか。