Smile Engineering Blog

ジェイエスピーからTipsや技術特集、プロジェクト物語を発信します

【Android】Moshi で JSON をパースする

今回は、Moshi を使って JSON をパースする方法を解説します。

なお、ここに掲載しているソースコードは以下の環境で動作確認しています。

  • Android Studio Bumblebee | 2021.1.1 Patch 2
  • JDK 11.0.11
  • Android Gradle Plugin 7.1.2
  • Kotlin 1.6.20
  • Gradle 7.4.2
  • KSP 1.6.20-1.0.4
  • com.squareup.moshi 1.13.0

ライブラリのインポート

最初に、Moshi を使用するために必要なライブラリをインポートします。

まず、今回は KSP を使用するため、KSP をインポートします。プロジェクトの build.gradle の pluginscom.google.devtools.ksp を追加します。

// Project's build.gradle
plugins {
    id 'com.google.devtools.ksp' version '1.6.20-1.0.4' apply false
}

そして、モジュールの build.gradle の plugins にも com.google.devtools.ksp を追加します。

// Module's build.gradle
plugins {
    id "com.google.devtools.ksp"
}

これで、KSP を使用できるようになります。

後は、Moshi を使用するためにモジュールの build.gradle の dependencies に以下を追加します。

// Module's build.gradle
dependencies {
    implementation "com.squareup.moshi:moshi:1.13.0"
    ksp "com.squareup.moshi:moshi-kotlin-codegen:1.13.0"
}

以上で、ライブラリのインポートは完了です。

データクラスの宣言

ここから、実装に入っていきます。

まず、パースする JSON のフォーマットをデータクラスで宣言する必要があります。今回、以下のような JSON の読み書きを行ってみます。

{
    "hidden_card": {
        "rank": "6",
        "suit": "SPADES"
    },
    "visible_cards": [
        {
            "rank": "4",
            "suit": "CLUBS"
        },
        {
            "rank": "A",
            "suit": "HEARTS"
        }
    ]
}

上記の JSON をデータクラスで宣言すると、以下のようになります。

@JsonClass(generateAdapter = true)
data class BlackjackHand(
    @Json(name = "hidden_card") val hiddenCard: Card,
    @Json(name = "visible_cards") val visibleCard: List<Card>,
)

@JsonClass(generateAdapter = true)
data class Card(
    val rank: Char,
    val suit: Suit,
)

enum class Suit {
    CLUBS, DIAMONDS, HEARTS, SPADES;
}

Moshi で使用するデータクラスには、@JsonClass(generateAdapter = true) を追加します。また、hidden_cardhiddenCard のように、JSON とデータクラスで名前が異なる場合、データクラスのプロパティに @Json を付与して、対応する JSON の名前を指定します。なお、JSON とデータクラスで名前が一致する場合は、@Json を省略できます。

以上で、データクラスの宣言は完了です。

インスタンスの生成

次に、JSON をパースするインスタンスを生成します。

val moshi = Moshi.Builder().build()
val jsonAdapter = moshi.adapter(BlackjackHand::class.java)

上記 1 行目の Moshi.Builder().build() にて Moshi のインスタンスを生成します。そして、2 行目で Moshi のインスタンスJSON のデータクラスを渡して、JsonAdapter を生成します。この JsonAdapter を使って、JSON をパースします。

JSON からオブジェクトにパースする

さきほど生成した JsonAdapter を使って JSON からオブジェクトにパースします。JsonAdapter#fromJson に JSON 文字列を渡すと、パースした後のオブジェクトを返してくれます。

val json = """
    {
        "hidden_card": {
            "rank": "6",
            "suit": "SPADES"
        },
        "visible_cards": [
            {
                "rank": "4",
                "suit": "CLUBS"
            },
            {
                "rank": "A",
                "suit": "HEARTS"
            }
        ]
    }
""".trimIndent()

val blackjackHand = jsonAdapter.fromJson(json)
Log.d(TAG, blackjackHand.toString())

上記を実行すると Logcat に以下のログが表示され、JSON からオブジェクトにパースが成功していることがわかります。

D/MainActivity: BlackjackHand(hiddenCard=Card(rank=6, suit=SPADES), visibleCard=[Card(rank=4, suit=CLUBS), Card(rank=A, suit=HEARTS)])

オブジェクトから JSON にパースする

逆に、オブジェクトから JSON にパースすることも可能です。JsonAdapter#toJson にオブジェクトを渡すと、パースした後の JSON を文字列で返してくれます。

val blackjackHand = BlackjackHand(
    Card('6', Suit.SPADES),
    listOf(Card('4', Suit.CLUBS), Card('A', Suit.HEARTS))
)

val json = jsonAdapter.toJson(blackjackHand)
Log.d(TAG, json)

上記を実行すると Logcat に以下のログが表示され、オブジェクトから JSON にパースが成功していることがわかります。

D/MainActivity: {"hidden_card":{"rank":"6","suit":"SPADES"},"visible_cards":[{"rank":"4","suit":"CLUBS"},{"rank":"A","suit":"HEARTS"}]}

参考

Moshi