Moshi 解析具有相同键但不同值的字段

Moshi parse fields with the same key but different values

我们的后端发送一个 json 响应,每次都可以包含相同键的不同值。

第一个变体示例

{
  "title": "2",
  "profile_image": {
    "profile_image_id": "4581542",
    "sized": "media/up/2020/48/460e689e286ca46b1006b62269ee21a22b6bdabf2496506e34e5d07c5d42c20f_user_photo_160674563705555.sized.JPG",
    "sized_w": "556",
    "sized_h": "555",
    "thumb": "media/up/2020/48/460e689e286ca46b1006b62269ee21a22b6bdabf2496506e34e5d07c5d42c20f_user_photo_160674563705555.thumb.JPG",
    "thumb_w": "90",
    "thumb_h": "90"
  },
  "profile": {
    "title": "2",
    "first_name": "John",
    "last_name": "Doe",
    "user_online_status": false
  }
}

下面是同一响应的另一种变体。请注意,profile_image 字段现在是数组类型,user_online_status 也可以是字符串或布尔值。此外,当 profile_image 为空时,它始终是一个空数组。在任何其他情况下都是对象。

{
  "title": "2",
  "profile_image": [],
  "profile": {
    "title": "2",
    "first_name": "John",
    "last_name": "Doe",
    "user_online_status": "0"
  }
}

如何在不创建额外模型的情况下使用 Retrofit 和 Moshi 解析此类响应 类?我可以使用自定义适配器吗?能举个例子吗?

您需要创建将手动反序列化 ProfileImage 属性的自定义适配器。当 Moshi 到达 profile_image 属性 时,将调用适配器。 您需要考虑的两种情况:

  1. 空情况 [] 这是 API 返回 json 数组时的情况。在这种情况下,您将收到 BEGIN_ARRAY 令牌。
  2. 所需的情况,您需要一个接一个地反序列化每个 ProfileImage 属性。这是您收到 BEGIN_OBJECT 令牌
  3. 的时候

必须保留案例的顺序,以确保始终首先检查空案例。

了解代币 here

class SkipEmptyProfileAdapter: JsonAdapter<ProfileImage>() {
    override fun fromJson(reader: JsonReader): ProfileImage? = when (reader.peek()) {
        JsonReader.Token.BEGIN_ARRAY -> {
           //this the case when [] is returned. Return null 
            null
        }
       
        JsonReader.Token.BEGIN_OBJECT -> {
           // here you parse the `profile_image` property 
           // here you start iterating properties from the json object until END_OBJECT is
          // found. By the time END_OBJECT is reached you should have populated the 
          // ProfileImage object 
        }
       
        else ->  null
    }

    override fun toJson(writer: JsonWriter, value: ProfileImage?) {
        writer.value(value ?: "[]")
    }
}

后端团队毕竟没有被解雇,我成了当天的英雄。

我对 profile_image 字段不一致的解决方案:

  1. 在我的数据 class
  2. 中,class 属性与 Any? 的类型不一致
  3. 实现了自定义 moshi 适配器
  4. 使用 Map 解析不一致的 json 字段。

class SkipEmptyProfileAdapter {

    @FromJson
    fun fromJson(response: UserProfileDataResponse): UserProfileDataResponse {
        if (response.profileImage is Map<*, *>) {
            val map = response.profileImage as Map<String, String>
            response.imageUrlParsed = map["sized"]
        }
        return response
    }
}

将适配器包含到 Moshi 生成器

fun provideMoshi(): Moshi {
    return Moshi.Builder()
        .add(SkipEmptyProfileAdapter()) //the ordering matters
        .add(KotlinJsonAdapterFactory())
        .build()
}