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

Kotlin中如何正确解析换行分隔的JSON数组(基于OkHttpClient)

规范解析逐行JSON响应的实现方案

问题场景

我在Kotlin应用中使用OkHttpClient向API上传文件进行处理,API会持续返回消息保持连接存活,直到处理完成。返回内容为每行一个独立的JSON对象(即JSON Lines格式),示例如下:

{"status":"IN_PROGRESS","transcript":null,"error":null}
{"status":"IN_PROGRESS","transcript":null,"error":null}
{"status":"IN_PROGRESS","transcript":null,"error":null}
{"status":"DONE","transcript":"Hello, world.","error":null}

此前通过字符串替换将其转为标准JSON数组再解析的方式,存在格式破坏风险(比如JSON字符串中包含}字符时会出错),需要更符合技术规范的实现方案。


方案一:逐行读取响应流并逐个解析

利用OkHttp响应体的字符流,逐行读取每个JSON对象,单独用Gson解析为Status实体。这种方式内存高效,且能实时处理每个状态更新。

代码实现

data class Status(
    val status: String?,
    val transcript: String?,
    val error: String?
)

fun main() {
    val client = OkHttpClient()
    val requestBody = MultipartBody.Builder()
        .setType(MultipartBody.FORM)
        // 添加上传文件及参数,示例:
        // .addFormDataPart("file", "audio.mp3", File("path/to/file").asRequestBody())
        .build()

    val request = Request.Builder()
        .url("http://localhost:8090")
        .post(requestBody)
        .build()

    client.newCall(request).execute().use { response ->
        if (!response.isSuccessful) throw IOException("请求失败: $response")

        val gson = Gson()
        val statusList = mutableListOf<Status>()

        // 逐行读取响应流
        response.body?.charStream()?.buffered()?.useLines { lines ->
            lines.forEach { line ->
                if (line.isNotBlank()) {
                    val status = gson.fromJson(line, Status::class.java)
                    statusList.add(status)

                    // 实时处理状态,比如打印进度或更新UI
                    when (status.status) {
                        "IN_PROGRESS" -> println("处理中...")
                        "DONE" -> println("处理完成,转录内容:${status.transcript}")
                    }
                }
            }
        }

        println("所有状态记录:$statusList")
    }
}

方案二:使用Gson流式解析(JsonReader)

如果API返回的JSON对象之间没有严格换行(或存在格式小瑕疵),可以用Gson的JsonReader进行流式解析,逐个读取JSON对象,兼容性更强。

代码实现

data class Status(
    val status: String?,
    val transcript: String?,
    val error: String?
)

fun main() {
    val client = OkHttpClient()
    val requestBody = MultipartBody.Builder()
        .setType(MultipartBody.FORM)
        // 添加上传文件及参数
        .build()

    val request = Request.Builder()
        .url("http://localhost:8090")
        .post(requestBody)
        .build()

    client.newCall(request).execute().use { response ->
        if (!response.isSuccessful) throw IOException("请求失败: $response")

        val gson = Gson()
        val statusList = mutableListOf<Status>()

        response.body?.charStream()?.use { charStream ->
            JsonReader(charStream).use { reader ->
                reader.isLenient = true // 兼容非严格JSON格式
                while (reader.hasNext()) {
                    val status = gson.fromJson(reader, Status::class.java)
                    statusList.add(status)

                    if (status.status == "DONE") {
                        println("处理完成:${status.transcript}")
                    }
                }
            }
        }

        println("所有状态记录:$statusList")
    }
}

方案优势

  • 避免格式错误:不会因为JSON字符串中包含}等特殊字符而破坏结构;
  • 内存高效:无需将整个响应读入内存,适合大响应或长连接场景;
  • 实时处理:可在收到每个状态时立即处理,无需等待完整响应返回。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 07:06:33