利用GSON和Retrofit解析嵌套多态对象

hgc7kmma  于 2022-11-06  发布在  其他
关注(0)|答案(2)|浏览(183)

我尝试显示具有不同类型ViewHolder(即文本、图像文本、视频等)的消息列表。我从API中获得了这些对象的列表,其格式如下:

{
 "message":"success",
 "total_pages":273,
 "current_page":1,
 "page_size":10,
 "notifications":[
  {
     "id":4214,
     "notification_message":"test notification 1",
     "meta_data":{
        "messageId":"19819189",
        "viewHolderType":"textOnly",
        "body":{
           "time":"10-06-21T02:31:29,573",
           "type":"notification",
           "title":"Hi, Welcome to the NT experience",
           "description":"This is the welcome message",
           "read":true
        }
     }
  },
  {
     "id":9811,
     "notification_message":"test vss notification",
     "meta_data":{
        "messageId":"2657652",
        "viewHolderType":"textWithImage",
        "body":{
           "time":"11-06-21T02:31:29,573",
           "type":"promotions",
           "title":"Your Package - Premium",
           "description":"Thank you for subscribing to the package. Your subscription entitles you to Premium 365 Days Plan (worth $76.61)",
           "headerImage":"www.someurl.com/image.jpg",
           "read":true
        }
     }
  }
 ]
}

现在我必须从网络模块中解析这个列表,以获取只使用 meta_data中的对象的客户端模块。为此,我创建了以下类:

open class BaseMessageListItem

internal data class MessageListResponse(
@field:SerializedName("current_page")
val current_page: Int,

@field:SerializedName("notifications")
val notifications: List<MessageListItem>,

@field:SerializedName("message")
val message: String,

@field:SerializedName("page_size")
val page_size: Int,

@field:SerializedName("total_page")
val total_page: Int
)

internal data class MessageListItem(
@field:SerializedName(“id”)
val id: String,

@field:SerializedName("notification_message")
val notification_message: String,

 @field:SerializedName("meta_data")
val meta_data: MessageListMetaDataItem,
)

internal data class MessageListMetaDataItem(
@field:SerializedName("messageId")
val messageId: String = "",

@field:SerializedName("viewHolderType")
val viewHolderType: String = "",

@field:SerializedName("body")
val body: BaseMessageListItem = BaseMessageListItem() 
)

internal data class ImageMessageListItem(
@field:SerializedName("description")
val description: String,

@field:SerializedName("headerImage")
val headerImage: String,

@field:SerializedName("read")
val read: Boolean,

@field:SerializedName("time")
val time: String,

@field:SerializedName("title")
val title: String,

@field:SerializedName("type")
val type: String
): BaseMessageListItem()

internal data class TextMessageListItem(
@field:SerializedName("description")
val description: String,

@field:SerializedName("read")
val read: Boolean,

@field:SerializedName("time")
val time: String,

@field:SerializedName("title")
val title: String,

@field:SerializedName("type")
val type: String
): BaseMessageListItem()

通知〉 meta_data〉body可以是多态的。我有一组类(用于ImageItem,ImageWithTextItem,VideoItem等),它们扩展到BaseMessageListItem。

private var runtimeTypeAdapterFactory: RuntimeTypeAdapterFactory<BaseMessageListItem> = RuntimeTypeAdapterFactory
    .of(BaseMessageListItem::class.java, "viewHolderType")
    .registerSubtype(ImageMessageListItem::class.java, MessageListItemTypes.TEXT_WITH_IMAGE.value)
    .registerSubtype(TextMessageListItem::class.java, MessageListItemTypes.TEXT_ONLY.value)

private var gson: Gson = GsonBuilder()
    .registerTypeAdapterFactory(runtimeTypeAdapterFactory)
    .create()

我尝试在RuntimeTypeAdapterFactory中使用viewHolderType解析它,但由于它不是BaseMessageListItem的属性,因此无法解析它。
任何一个有任何经验处理这种类型的JSON,请分享任何指针。

r1zhe5dt

r1zhe5dt1#

RuntimeTypeAdapterFactory要求将viewHolderType字段直接放入body对象中。为了解决此问题,您可以使用修补程序RuntimeTypeAdapterFactory(它甚至没有作为编译后的JAR发布,而是仍然作为源代码保留在公共存储库中,可以自由修改),或者修复类层次结构以提升缺少的字段,因为它只能处理同一嵌套级别上的字段。

internal var gson: Gson = GsonBuilder()
        .registerTypeAdapterFactory(
                RuntimeTypeAdapterFactory.of(BaseMessageListMetaDataItem::class.java, "viewHolderType")
                        .registerSubtype(TextWithImageMessageListMetaDataItem::class.java, "textWithImage")
                        .registerSubtype(TextOnlyMessageListMetaDataItem::class.java, "textOnly")
        )
        .create()

internal data class MessageListItem(
        @field:SerializedName("meta_data")
        val metaData: BaseMessageListMetaDataItem<*>?,
)

internal abstract class BaseMessageListMetaDataItem<out T>(
        @field:SerializedName("viewHolderType")
        val viewHolderType: String?,
        @field:SerializedName("body")
        val body: T?
) where T : BaseMessageListMetaDataItem.Body {

    internal abstract class Body

}

internal class TextOnlyMessageListMetaDataItem
    : BaseMessageListMetaDataItem<TextOnlyMessageListMetaDataItem.Body>(null, null) {

    internal data class Body(
            @field:SerializedName("title")
            val title: String?
    ) : BaseMessageListMetaDataItem.Body()

}

internal class TextWithImageMessageListMetaDataItem
    : BaseMessageListMetaDataItem<TextWithImageMessageListMetaDataItem.Body>(null, null) {

    internal data class Body(
            @field:SerializedName("title")
            val title: String?,
            @field:SerializedName("headerImage")
            val headerImage: String?
    ) : BaseMessageListMetaDataItem.Body()

}
ffx8fchx

ffx8fchx2#

我可能理解错了,但我想建议一种不同的方法。我假设你想直接从API响应中得到一个ViewHolder类型。
我建议采取两种办法:

  • 首先,如果可以修改API响应,我建议将viewHolderType从String更改为Int,这样您就可以清楚地了解Map,然后可以直接比较它。
  • 其次,我建议在数据类中保留另一个键,它根据接收到的viewHolderType设置值,如下所示。
internal data class MessageListMetaDataItem(
        @field:SerializedName("messageId")
        val messageId: String = "",

        @field:SerializedName("viewHolderType")
        val viewHolderType: String = "",

        @field:SerializedName("body")
        val body: BaseMessageListItem = BaseMessageListItem()
) {
    val viewHolderMapping: Int
    get() = when(viewHolderType){
        "textOnly" -> MessageListItemTypes.TEXT_ONLY
        "textWithImage" -> MessageListItemTypes.TEXT_WITH_IMAGE
        else -> MessageListItemTypes.UNKNOWN_TYPE
    }
}

相关问题