You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Kotlin中解析嵌套数组结构的JSON数据

解决JSON数组数组到Kotlin数据类的解析问题

你的JSON结构是数组的数组(每个元素是包含8个元素的有序数组,对应TermBank的8个字段),但Gson和Kotlin序列化默认都是按键值对格式的JSON对象来解析数据类的,所以才会出现"Expected BEGIN_OBJECT but was BEGIN_ARRAY"的错误。下面分别给出两种库的解决方法:


方案一:用Gson自定义TypeAdapter

写一个TypeAdapter,手动把数组的每个元素映射到TermBank的字段:

import com.google.gson.TypeAdapter
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonWriter
import com.google.gson.JsonSyntaxException

class TermBankTypeAdapter : TypeAdapter<TermBank>() {
    override fun write(out: JsonWriter?, value: TermBank?) {
        // 若不需要序列化JSON,此方法可留空;如需实现,按数组格式写入即可
        out?.apply {
            beginArray()
            value?.let {
                value(it.text)
                value(it.reading)
                value(it.tags)
                value(it.rules)
                value(it.popularity)
                beginArray()
                it.definition.forEach { def -> value(def) }
                endArray()
                value(it.sequenceNumber)
                value(it.tags2)
            }
            endArray()
        }
    }

    override fun read(`in`: JsonReader?): TermBank {
        return `in`?.run {
            beginArray()
            // 按JSON数组的顺序依次读取每个字段
            val text = nextString()
            val reading = nextString()
            val tags = nextString()
            val rules = nextString()
            val popularity = nextInt()
            
            // 解析嵌套的definition数组
            beginArray()
            val definition = mutableListOf<String>()
            while (hasNext()) {
                definition.add(nextString())
            }
            endArray()
            
            val sequenceNumber = nextInt()
            val tags2 = nextString()
            endArray()
            
            TermBank(text, reading, tags, rules, popularity, definition, sequenceNumber, tags2)
        } ?: throw JsonSyntaxException("无效的JSON输入")
    }
}

使用这个Adapter解析:

import com.google.gson.GsonBuilder

val gson = GsonBuilder()
    .registerTypeAdapter(TermBank::class.java, TermBankTypeAdapter())
    .create()
val termBankList = gson.fromJson<Array<TermBank>>(fileString, Array<TermBank>::class.java)

方案二:用Kotlin序列化自定义Serializer

首先给数据类加上序列化注解,并指定自定义的Serializer:

import kotlinx.serialization.Serializable
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.descriptors.element
import kotlinx.serialization.encoding.CompositeDecoder
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.encoding.decodeStructure
import kotlinx.serialization.json.Json
import kotlinx.serialization.serializer

@Serializable(with = TermBankSerializer::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,
)

object TermBankSerializer : KSerializer<TermBank> {
    override val descriptor: SerialDescriptor = buildClassSerialDescriptor("TermBank") {
        element<String>("text")
        element<String>("reading")
        element<String>("tags")
        element<String>("rules")
        element<Int>("popularity")
        element<List<String>>("definition")
        element<Int>("sequenceNumber")
        element<String>("tags2")
    }

    override fun serialize(encoder: Encoder, value: TermBank) {
        // 无需序列化的话,此方法可留空
    }

    override fun deserialize(decoder: Decoder): TermBank {
        return decoder.decodeStructure(descriptor) {
            var text = ""
            var reading = ""
            var tags = ""
            var rules = ""
            var popularity = 0
            var definition = emptyList<String>()
            var sequenceNumber = 0
            var tags2 = ""

            // 按数组顺序读取所有元素
            if (decodeSequentially()) {
                text = decodeStringElement(descriptor, 0)
                reading = decodeStringElement(descriptor, 1)
                tags = decodeStringElement(descriptor, 2)
                rules = decodeStringElement(descriptor, 3)
                popularity = decodeIntElement(descriptor, 4)
                definition = decodeSerializableElement(descriptor, 5, ListSerializer(String.serializer()))
                sequenceNumber = decodeIntElement(descriptor, 6)
                tags2 = decodeStringElement(descriptor, 7)
            } else {
                // 处理非顺序解析的情况(按索引读取)
                while (true) {
                    when (val index = decodeElementIndex(descriptor)) {
                        0 -> text = decodeStringElement(descriptor, index)
                        1 -> reading = decodeStringElement(descriptor, index)
                        2 -> tags = decodeStringElement(descriptor, index)
                        3 -> rules = decodeStringElement(descriptor, index)
                        4 -> popularity = decodeIntElement(descriptor, index)
                        5 -> definition = decodeSerializableElement(descriptor, index, ListSerializer(String.serializer()))
                        6 -> sequenceNumber = decodeIntElement(descriptor, index)
                        7 -> tags2 = decodeStringElement(descriptor, index)
                        CompositeDecoder.DECODE_DONE -> break
                        else -> error("意外的索引:$index")
                    }
                }
            }
            TermBank(text, reading, tags, rules, popularity, definition, sequenceNumber, tags2)
        }
    }
}

最后执行解析:

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

内容的提问来源于stack exchange,提问作者Noah

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 21:11:07