首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用GSON和Retrofit解析嵌套多态对象

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

Stack Overflow用户
提问于 2021-06-03 06:25:35
回答 2查看 215关注 0票数 1

我试图显示具有不同类型的ViewHolders (文本、ImageText、视频等)的消息列表。我从API中得到了一些类似于这种格式的对象列表:

代码语言:javascript
复制
{
 "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中的对象。为此,我创建了以下类:

代码语言:javascript
复制
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()

notifications>meta_data>body可以是多态的。我有一组类(用于ImageItem、ImageWithTextItem、VideoItem等),扩展到BaseMessageListItem。

代码语言:javascript
复制
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()

我尝试使用viewHolderTypeRuntimeTypeAdapterFactory中解析它,但是由于它不是BaseMessageListItem的属性,所以它无法解析它。

任何有处理这类JSON的经验的人,请分享任何的指针。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-06-03 10:13:24

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

代码语言:javascript
复制
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()

}
票数 1
EN

Stack Overflow用户

发布于 2021-06-03 06:35:53

我可能理解错了,但我想提出一个不同的方法。我假设您希望从您的API响应中直接获得一个ViewHolder类型。

有两种方法我想建议:

  • 首先,如果可以修改API响应,我建议将viewHolderType从字符串更改为Int,这样您就可以清楚地进行映射,然后直接进行比较。
  • 第二,我建议在数据类中保留另一个键,根据它接收到的值来设置值,如下所示。

代码语言:javascript
复制
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
    }
}
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67816386

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档