Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
67
Help us understand the problem. What is going on with this article?
@egugue

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

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

67
Help us understand the problem. What is going on with this article?
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
egugue

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
67
Help us understand the problem. What is going on with this article?