使用 GSON 和 Retrofit 解析嵌套的多态对象 [英] Parsing Nested Polymorphic Objects with GSON and Retrofit
问题描述
我正在尝试显示具有不同类型 ViewHolder(即文本、图像文本、视频等)的消息列表.我以这种格式从 API 中获取了这些对象的列表:
I am trying to show list of messages with different types of ViewHolders i.e. Text, ImageText, Video etc. I get a list of these objects from API somewhat in this format:
{
"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
}
}
}
]
}
现在我必须从客户端模块的网络模块解析这个列表,它将只使用元数据中的对象.为此,我创建了以下类:
Now I have to parse this list from network module for client module which will use only the objects inside meta_data. To that end I have created following classes:
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()
通知>元数据>主体可以是多态的.我有一组扩展到 BaseMessageListItem 的类(用于 ImageItem、ImageWithTextItem、VideoItem 等).
The notifications>meta_data>body can be polymorphic. I have set of classes (for ImageItem, ImageWithTextItem, VideoItem etc) which extend to 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
的属性,因此无法解析它.
I tried parsing it using viewHolderType
in RuntimeTypeAdapterFactory
but since it's not a property of BaseMessageListItem
, it is not able to parse it.
任何人都有处理这种类型的 JSON
的经验,请分享任何指针.
Any one has any experience dealing with this type of JSON
, please do share any pointers.
推荐答案
RuntimeTypeAdapterFactory
需要将 viewHolderType
字段直接放入 body
对象.为了解决这个问题,你有要么修补 RuntimeTypeAdapterFactory
(它甚至不作为编译的 JAR 发布,而是作为源代码自由修改仍保留在公共存储库中),或者修复您的类层次结构以提升缺失的字段,因为它只能处理同一嵌套级别的字段.
RuntimeTypeAdapterFactory
requires the viewHolderType
field to be put right into the body
objects. In order to fix this, you have
either patch RuntimeTypeAdapterFactory
(it is not even published as a compiled JAR, but rather still retains in the public repository as source code free to modify), or fix your class hierarchy to lift up the missing field because it can only work with fields on the same nest level.
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()
}
这篇关于使用 GSON 和 Retrofit 解析嵌套的多态对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!