如何使用改造和 moshi 解析换行符分隔的 JSON?

Posted

技术标签:

【中文标题】如何使用改造和 moshi 解析换行符分隔的 JSON?【英文标题】:How to parse newline delimited JSON with retrofit and moshi? 【发布时间】:2021-06-27 15:27:41 【问题描述】:

我正在尝试使用改造和 moshi 解析换行符分隔的 json。这是我的 GET 函数:

suspend fun getDeviceValuesNew(@Path("application-id") applicationId: String, @Path("device-id") deviceId: String) 
: Response<List<ValueApiResponse>>

当我尝试运行它时,我得到了这个错误:

 com.squareup.moshi.JsonDataException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at path $

HTTP 调用返回的 json 格式如下:


    "result": 
        "end_device_ids": 
            "device_id": "esp32",
            "application_ids": 
        ,
        "received_at": "2021-03-31T11:33:42.757281753Z",
        "uplink_message": 
            "decoded_payload": 
                "brightness": 0
            ,
            "settings": 
                "data_rate": 
            ,
            "received_at": "2021-03-31T11:33:42.547285090Z"
        
    


    "result": 
        "end_device_ids": 
            "device_id": "esp32",
            "application_ids": 
        ,
        "received_at": "2021-03-31T11:18:17.745921472Z",
        "uplink_message": 
            "decoded_payload": 
                "brightness": 0
            ,
            "settings": 
                "data_rate": 
            ,
            "received_at": "2021-03-31T11:18:17.538276218Z"
        
    

编辑#1: 正如您在下面的回答中看到的那样,我设法从 API 获得了有效的 JSON 响应,但我仍在努力将这些 JSON 对象解析为 Kotlin 对象列表。 如何让 Moshi 将这些换行符分隔的 JSON 对象作为列表处理? 我认为问题在于 Moshi 需要将对象包装在数组中才能被识别为列表。我该怎么做?

这是我用来解析的数据类:

@JsonClass(generateAdapter = true)
data class ValueDto(
    @Json(name = "result")
    val result: Result
) 
    @JsonClass(generateAdapter = true)
    data class Result(
        @Json(name = "end_device_ids")
        val endDeviceIds: EndDeviceIds,
        @Json(name = "received_at")
        val receivedAt: String,
        @Json(name = "uplink_message")
        val uplinkMessage: UplinkMessage
    ) 
        @JsonClass(generateAdapter = true)
        data class EndDeviceIds(
            @Json(name = "application_ids")
            val applicationIds: ApplicationIds,
            @Json(name = "device_id")
            val deviceId: String
        ) 
            @JsonClass(generateAdapter = true)
            class ApplicationIds(
            )
        

        @JsonClass(generateAdapter = true)
        data class UplinkMessage(
            @Json(name = "decoded_payload")
            val decodedPayload: DecodedPayload,
            @Json(name = "received_at")
            val receivedAt: String,
            @Json(name = "settings")
            val settings: Settings
        ) 
            @JsonClass(generateAdapter = true)
            data class DecodedPayload(
                @Json(name = "brightness")
                val brightness: Int
            )
            @JsonClass(generateAdapter = true)
            data class Settings(
                @Json(name = "data_rate")
                val dataRate: DataRate
            ) 
                @JsonClass(generateAdapter = true)
                class DataRate(
                )
            
        
    

【问题讨论】:

那不是有效的 json,因此您将不得不以某种方式拆分对象,我真的不知道您将如何做到这一点,因为每一行都有一个换行符。通常是这样的,每个对象后面只有一个换行符,所以你在换行符上拆分,你得到每个对象 【参考方案1】:

@tyczj 是对的,这不是有效的 ndjson,因为在 ndjson 中,换行符只出现在每个单独的 json 文本之后。解决方案是使用 HTTP 请求发送Accept: text/event-stream,现在我得到一个有效的 ndjson 响应,如下所示:

"result":"end_device_ids":"device_id":"esp32-bh1750","application_ids":,"received_at":"2021-04-24T10:19:04.021238048Z","uplink_message":"decoded_payload":"brightness":0,"settings":"data_rate":,"received_at":"2021-04-24T10:19:03.809173924Z"

"result":"end_device_ids":"device_id":"esp32-bh1750","application_ids":,"received_at":"2021-04-24T10:25:22.260712161Z","uplink_message":"decoded_payload":"brightness":119,"settings":"data_rate":,"received_at":"2021-04-24T10:25:22.046086937Z"

"result":"end_device_ids":"device_id":"esp32-bh1750","application_ids":,"received_at":"2021-04-24T10:18:58.438947740Z","uplink_message":"decoded_payload":"brightness":0,"settings":"data_rate":,"received_at":"2021-04-24T10:18:58.228671174Z"

"result":"end_device_ids":"device_id":"esp32-bh1750","application_ids":,"received_at":"2021-04-24T10:21:10.102303310Z","uplink_message":"decoded_payload":"brightness":106,"settings":"data_rate":,"received_at":"2021-04-24T10:21:09.893217735Z"

"result":"end_device_ids":"device_id":"esp32-bh1750","application_ids":,"received_at":"2021-04-24T10:23:16.177064041Z","uplink_message":"decoded_payload":"brightness":108,"settings":"data_rate":,"received_at":"2021-04-24T10:23:15.967959055Z"

"result":"end_device_ids":"device_id":"esp32-bh1750","application_ids":,"received_at":"2021-04-24T10:27:28.334312076Z","uplink_message":"decoded_payload":"brightness":117,"settings":"data_rate":,"received_at":"2021-04-24T10:27:28.126104222Z"

"result":"end_device_ids":"device_id":"esp32-bh1750","application_ids":,"received_at":"2021-04-24T10:29:34.400253264Z","uplink_message":"decoded_payload":"brightness":99,"settings":"data_rate":,"received_at":"2021-04-24T10:29:34.190980301Z"

"result":"end_device_ids":"device_id":"esp32-bh1750","application_ids":,"received_at":"2021-04-24T10:31:40.481766225Z","uplink_message":"decoded_payload":"brightness":118,"settings":"data_rate":,"received_at":"2021-04-24T10:31:40.270452429Z"

"result":"end_device_ids":"device_id":"esp32-bh1750","application_ids":,"received_at":"2021-04-24T10:33:46.567235913Z","uplink_message":"decoded_payload":"brightness":114,"settings":"data_rate":,"received_at":"2021-04-24T10:33:46.357373037Z"

"result":"end_device_ids":"device_id":"esp32-bh1750","application_ids":,"received_at":"2021-04-24T10:35:52.737386496Z","uplink_message":"decoded_payload":"brightness":121,"settings":"data_rate":,"received_at":"2021-04-24T10:35:52.426583804Z"

我已经通过改造注释添加了标题: @Headers("Accept: text/event-stream ")

【讨论】:

以上是关于如何使用改造和 moshi 解析换行符分隔的 JSON?的主要内容,如果未能解决你的问题,请参考以下文章

如何通过node.js中的JSONStream模块解析一个大的、换行符分隔的JSON文件?

在 Kotlin 中使用 Moshi 和 Retrofit 解析具有增量对象名称的 JSON

如何在 moshi (kotlin) 中解析 LinkedHashMap

使用 Moshi 和 Retrofit 将响应包装在另一个对象中

Square新作,新一代Json解析库Moshi使用及原理解析

如何在 Moshi 中跳过 JSON 属性?