Edited at

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

More than 1 year has passed since last update.


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