67
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

posted at

updated at

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なpropertynullを渡した場合は例外がthrowされ、classが不正な状態になることを自動で防いでくれます。

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

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

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

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

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の開発に至ったそうです。参考
そのような経緯もあり、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します。

app/build.gradle
// dependenciesに以下を追加
def moshiVerion = '1.5.0'
compile "com.squareup.moshi:moshi:$moshiVerion"
compile "com.squareup.moshi:moshi-kotlin:$moshiVerion"
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される」ようになります。

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の導入を検討されてはいかがでしょうか。

Register as a new user and use Qiita more conveniently

  1. You can follow users and tags
  2. you can stock useful information
  3. You can make editorial suggestions for articles
What you can do with signing up
67
Help us understand the problem. What are the problem?