Search code examples
androidjsonkotlinserializationgson

How to parse a json array in Kotlin


I have a json in a val filestring: String, like

[
    [
        "あ",
        "",
        "",
        "",
        0,
        [
            "あ\n(1)五十音図ア行第一段の仮名。後舌の広母音。\n(2)平仮名「あ」は「安」の草体。片仮名「ア」は「阿」の行書体の偏。\n"
        ],
        0,
        ""
    ],
    [
        "足",
        "あ",
        "",
        "",
        0,
        [
            "あ 【足】\nあし。「―の音せず行かむ駒もが/万葉 3387」\n〔多く「足掻(アガ)き」「足結(アユイ)」など,複合した形で見られる〕\n"
        ],
        1,
        ""
    ],
    ...
]

and want to parse it to kotlin.

I have a data class

data class TermBank (
    val text: String,
    val reading: String,
    val tags: String,
    val rules: String,
    val popularity: Int,
    val definition: List<String>,
    val sequenceNumber: Int,
    val tags2: String,
)

I have tried this code with the gson library

val obj = Gson().fromJson(fileString, Array<TermBank>::class.java)

and get the error com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 3 path $[0]

I have also tried the kotlin serialization library

val obj = Json.decodeFromString<Array<TermBank>>(fileString)

and get the error kotlinx.serialization.json.internal.JsonDecodingException: Unexpected JSON token at offset 0: Expected start of the object '{', but had '[' instead at path: $[0] JSON input: [["あ","","","",0,["あ\n(1)五十音図ア.....

Edit: As I understand, my problem stems from my format being an array of [string, string, string, string, int, string array, int, string] arrays. I am unsure how to parse a json of this form.

I can also not think of how to do this with .split as each array has commas in it so .split(',') won't work


Solution

  • You can create your own Deserializer to convert those Arrays of String to your TermBank object:

    class CustomDeserializer : JsonDeserializer<TermBank> {
      override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?): TermBank {
        return if (json?.isJsonArray == true) {
          val array = json.asJsonArray
          TermBank(
            array.get(0).asString,
            array.get(1).asString,
            array.get(2).asString,
            array.get(3).asString,
            array.get(4).asInt,
            array.get(5).asString.split("\n"), // Not sure how you split here
            array.get(6).asInt,
            array.get(7).asString)
        } else {
          TermBank("", "", "", "", 0, listOf(), 0, "")
        }
      }
    }
    

    And you can build Gson with your Deserializer to convert your JSON into List<TermBank>:

    val gson = GsonBuilder().registerTypeAdapter(TermBank::class.java, CustomDeserializer()).create()
    val type = object : TypeToken<List<TermBank>>() {}.type
    val target = gson.fromJson<List<TermBank>>(arrayJson, type)
    println(target.toString())
    

    You should have output like this:

    [TermBank(text=あ, reading=, tags=, rules=, popularity=0, definition=[あ, (1)五十音図ア行第一段の仮名。後舌の広母音。, (2)平仮名「あ」は「安」の草体。片仮名「ア」は「阿」の行書体の偏。, ], sequenceNumber=0, tags2=), TermBank(text=足, reading=あ, tags=, rules=, popularity=0, definition=[あ 【足】, あし。「―の音せず行かむ駒もが/万葉 3387」, 〔多く「足掻(アガ)き」「足結(アユイ)」など,複合した形で見られる〕, ], sequenceNumber=1, tags2=)]