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

如何无需代理类,用复合描述符解析嵌套JSON到Kotlin数据类?

Kotlin序列化:无需代理类,仅用复合描述符解析嵌套JSON?

我定义了两个Kotlin数据类Song和Thumbnail:

data class Song(
    val title: String,
    val year: Int,
    val watchId: String,
    val thumbnails: List<Thumbnail>
)

data class Thumbnail(
    val url: String,
    val width: Int,
    val height: Int
)

需要解析的JSON结构如下(仅关注search.result下的列表数据,其余字段无需处理):

{
  "search": {
    "result": [
      {
        "title": "Only Time",
        "year": 2000,
        "watchId": "ifgihvidvcs",
        "thumbnails": [{"width": 60, "height": 60, "url": "url"}]
      }
    ]
    // 其他无需处理的数据
  }
  // 其他无需处理的数据
}

我目前只了解通过编写代理类及对应描述符来处理这种嵌套结构的方法,想请教:是否可以无需编写代理类,仅通过编写复合描述符来实现解析?


可以,完全不用额外写代理类,直接给List<Song>自定义一个序列化器,在序列化器里构建复合描述符并处理嵌套的JSON路径即可。

实现步骤

  1. 为List<Song>创建自定义序列化器,继承KSerializer<List<Song>>
  2. 在descriptor中构建复合描述符,对应JSON的嵌套层级
  3. 在deserialize方法中跳过外层无关节点,直接解析search.result下的数组

完整代码示例

import kotlinx.serialization.*
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
import kotlinx.serialization.json.*

@Serializable
data class Song(
    val title: String,
    val year: Int,
    val watchId: String,
    val thumbnails: List<Thumbnail>
)

@Serializable
data class Thumbnail(
    val url: String,
    val width: Int,
    val height: Int
)

// 自定义List<Song>的序列化器
object SongListSerializer : KSerializer<List<Song>> {
    // 构建复合描述符,对应JSON的search -> result层级
    override val descriptor: SerialDescriptor = buildClassSerialDescriptor("SongList") {
        element("search", buildClassSerialDescriptor("Search") {
            element("result", ListSerializer(Song.serializer()).descriptor)
        })
    }

    override fun serialize(encoder: Encoder, value: List<Song>) {
        // 若需序列化可按反向逻辑构建JSON,此处仅关注解析可简化
        val jsonEncoder = encoder as JsonEncoder
        jsonEncoder.encodeJsonElement(
            buildJsonObject {
                putJsonObject("search") {
                    putJsonArray("result") {
                        value.forEach { add(Json.encodeToJsonElement(it)) }
                    }
                }
            }
        )
    }

    override fun deserialize(decoder: Decoder): List<Song> {
        val jsonDecoder = decoder as JsonDecoder
        val root = jsonDecoder.decodeJsonElement() as JsonObject
        
        // 直接定位到目标数组节点
        val resultArray = root["search"]?.jsonObject?.get("result")?.jsonArray
            ?: throw SerializationException("JSON缺少search.result节点")
        
        // 解析数组中的每个Song对象
        return resultArray.map { Json.decodeFromJsonElement(Song.serializer(), it) }
    }
}

// 使用示例
fun main() {
    val json = """
    {
      "search": {
        "result": [
          {
            "title": "Only Time",
            "year": 2000,
            "watchId": "ifgihvidvcs",
            "thumbnails": [{"width": 60, "height": 60, "url": "test-url"}]
          }
        ]
      }
    }
    """.trimIndent()

    val songList = Json.decodeFromString(SongListSerializer, json)
    println(songList.first().title) // 输出 Only Time
}

关键说明

  • 复合描述符通过buildClassSerialDescriptor嵌套构建,准确映射JSON的层级结构,让序列化框架能正确识别数据格式
  • 解析时直接从JSON根节点定位到目标数组,无需额外代理类包裹外层结构
  • 整个实现仅依赖自定义序列化器和复合描述符,没有引入冗余的类结构

内容的提问来源于stack exchange,提问作者Бодя павук

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 13:17:40