Home About Contact
Kotlin , JSON , JavaScript

Kotlin serialization で JSON を扱う その2

前回は、ごく簡単な json データを扱っただけなので、 今回は、もう少し複雑かつ実践的な json データを kotlinx serialization を使ってパースしてみます。

最終的にパースしたい json データはこれです。

val jsonString = """{
  "list": [
    { "type": "electric",
      "pokemons": [
        { "name": "Pikachu" },
        { "name": "Raichu" },
        { "name": "Voltorb" },
        { "name": "Electrode" }]},
    { "type": "water",
      "pokemons": [
        { "name": "Squirtle" },
        { "name": "Wartortle" },
        { "name": "Psyduck" },
        { "name": "Golduck" }]},
    { "type": "fire",
      "pokemons": [
        { "name": "Charmander" },
        { "name": "Charmeleon" },
        { "name": "Ponyta" }]},
    { "type": "rock",
      "pokemons": [
        { "name": "Geodude" },
        { "name": "Graveler" },
        { "name": "Golem" }]}
  ]
}"""

まずは、話を簡単にするために以下の部分だけパースすることを考えます。

    { "type": "electric",
      "pokemons": [
        { "name": "Pikachu" },
        { "name": "Raichu" },
        { "name": "Voltorb" },
        { "name": "Electrode" }]}

以下のコードを書きます。

json.main.kts

@file:Repository("https://repo1.maven.org/maven2/")
@file:DependsOn("org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.4.1")

import kotlinx.serialization.json.*

val jsonString = """{
  "type": "electric",
  "pokemons": [
    { "name": "Pikachu" },
    { "name": "Raichu" },
    { "name": "Voltorb" },
    { "name": "Electrode" }]}"""

val rootElement: JsonElement = Json.parseToJsonElement(jsonString)
val typeElement: JsonElement? = rootElement.jsonObject["type"]
println(typeElement)

実行。

$ kotlinc -script json.main.kts
"electric"

うまくいきました。

typeElement から electric 文字列を取り出すには、以下のようにします。

val typeValue: String? = typeElement?.jsonPrimitive?.contentOrNull

typeValue が決して null にならないようにしたければ、以下のようにします。

val typeValue: String = typeElement?.jsonPrimitive?.contentOrNull?:""

もし null だった場合、空文字列を返します。

次に、pokemons の部分の処理を方法を考えます。 先ほどは値が文字列でしたが、ここはリストになっているので、次のようにします。

val array: JsonArray? = rootElement.jsonObject["pokemons"]?.jsonArray

array の中身を確認したければ、次のようにします。

if(array!=null){
    array.forEach {
        println(it)
    }
}

JsonArray? という型は扱いづらいので toList という補助関数を導入します。 これは JsonArray? を List<JsonElement> に変換する関数です。

val toList: (JsonArray?)->List<JsonElement> = { array->
    if( array==null ){
        listOf()
    }
    else {
        0.until(array.size).map { index->
            array.get(index)
        }
    }
}

作動を確認します。

val array: JsonArray? = rootElement.jsonObject["pokemons"]?.jsonArray
println(toList(array))

実行:

$ kotlinc -script json.main.kts
[{"name":"Pikachu"}, {"name":"Raichu"}, {"name":"Voltorb"}, {"name":"Electrode"}]

うまくいきました。

toList して戻ってきた値は List<JsonElement> なので、この中身をパースするには次のようにします。

toList(array).forEach { item->
    val nameValue: String = item.jsonObject["name"]?.jsonPrimitive?.contentOrNull?:""
    println("- ${nameValue}")
}

ここでは jsonPrimitivie が String の場合だけを扱っていますが、 もし Boolean, Int, Long, Float, Double などの場合は booleanOrNull, intOrNull, ... を使います。

詳しくはこのページ https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-primitive/ を参照。

ここまでのコードをまとめます。

// json.main.kts
@file:Repository("https://repo1.maven.org/maven2/")
@file:DependsOn("org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.4.1")

import kotlinx.serialization.json.*

val jsonString = """{
  "type": "electric",
  "pokemons": [
    { "name": "Pikachu" },
    { "name": "Raichu" },
    { "name": "Voltorb" },
    { "name": "Electrode" }]}"""


val toList: (JsonArray?)->List<JsonElement> = { array->
    if( array==null ){
        listOf()
    }
    else {
        0.until(array.size).map { index->
            array.get(index)
        }
    }
}

val rootElement: JsonElement = Json.parseToJsonElement(jsonString)
val typeElement: JsonElement? = rootElement.jsonObject["type"]
println(typeElement)

val typeValue: String? = typeElement?.jsonPrimitive?.contentOrNull
println(typeValue)

val array: JsonArray? = rootElement.jsonObject["pokemons"]?.jsonArray
toList(array).forEach { item->
    val nameValue: String = item.jsonObject["name"]?.jsonPrimitive?.contentOrNull?:""
    println("- ${nameValue}")
}

では、最終的にパースしたかった json データをパースします。

といっても、ここまででパースした部分が list: [{},{},...] のように list に入れてあるだけなので、 次のようにするだけの話です。

val rootElement: JsonElement = Json.parseToJsonElement(jsonString)
val list0 = toList(rootElement.jsonObject["list"]?.jsonArray)
println(list0)

list0 の各 element が先ほど上でパースした内容になる。

list0.forEach { element->
    // 先ほど書いたコード
}

実行します。

$ kotlinc -script json.main.kts
electric
- Pikachu
- Raichu
- Voltorb
- Electrode
water
- Squirtle
- Wartortle
- Psyduck
- Golduck
fire
- Charmander
- Charmeleon
- Ponyta
rock
- Geodude
- Graveler
- Golem

まとめ

完成したコードを掲載します。

// json.main.kts

@file:Repository("https://repo1.maven.org/maven2/")
@file:DependsOn("org.jetbrains.kotlinx:kotlinx-serialization-json-jvm:1.4.1")

import kotlinx.serialization.json.*

val jsonString = """{
  "list": [
    { "type": "electric",
      "pokemons": [
        { "name": "Pikachu" },
        { "name": "Raichu" },
        { "name": "Voltorb" },
        { "name": "Electrode" }]},
    { "type": "water",
      "pokemons": [
        { "name": "Squirtle" },
        { "name": "Wartortle" },
        { "name": "Psyduck" },
        { "name": "Golduck" }]},
    { "type": "fire",
      "pokemons": [
        { "name": "Charmander" },
        { "name": "Charmeleon" },
        { "name": "Ponyta" }]},
    { "type": "rock",
      "pokemons": [
        { "name": "Geodude" },
        { "name": "Graveler" },
        { "name": "Golem" }]}
  ]
}"""


val toList: (JsonArray?)->List<JsonElement> = { array->
    if( array==null ){
        listOf()
    }
    else {
        0.until(array.size).map { index->
            array.get(index)
        }
    }
}

val rootElement: JsonElement = Json.parseToJsonElement(jsonString)
val list0 = toList(rootElement.jsonObject["list"]?.jsonArray)
list0.forEach { element->
    val typeElement: JsonElement? = element.jsonObject["type"]
    val typeValue: String? = typeElement?.jsonPrimitive?.contentOrNull
    println(typeValue)
    
    val array: JsonArray? = element.jsonObject["pokemons"]?.jsonArray
    toList(array).forEach { item->
        val nameValue: String = item.jsonObject["name"]?.jsonPrimitive?.contentOrNull?:""
        println("- ${nameValue}")
    }
}

以上です。