【问题标题】:Moshi parse fields with the same key but different valuesMoshi 解析具有相同键但不同值的字段
【发布时间】:2020-11-30 15:43:57
【问题描述】:

我们的后端每次都会发送一个 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 字段现在是 Array 类型,user_online_status 也可以是字符串或布尔值。此外,当 profile_image 为空时,它始终是一个空数组。在任何其他情况下,它都是对象。

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

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

【问题讨论】:

  • 您是否尝试过要求 API 开发人员保持一致?
  • 请你的上级解雇整个后端团队,从他们的领导开始。
  • 爱 cmets。
  • 没有时间解雇后端团队,也没有时间让他们解决我们明天要交付的问题,我快疯了。

标签: android retrofit2 moshi


【解决方案1】:

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

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

  1. 在我的数据类中将不一致的类属性标记为 Any? 类型
  2. 实现了自定义 moshi 适配器
  3. 利用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 builder

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

【讨论】:

    【解决方案2】:

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

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

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

    阅读令牌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 为空时,它始终是一个空数组。在任何其他情况下,它都是对象。还有一件事情。我是否必须在profile 内为user_online_status 创建另一个适配器?
    • 运气不好,当通过 Mosh builder 上的 addAdapter 添加时,适配器似乎没有被触发
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-18
    • 1970-01-01
    • 1970-01-01
    • 2015-02-15
    相关资源
    最近更新 更多