android - 使用 GSON 和 Retrofit 解析嵌套的多态对象
问题描述
我正在尝试显示具有不同类型 ViewHolders 的消息列表,即文本、ImageText、视频等。我从 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()
通知>元数据>正文可以是多态的。我有一组延伸到 BaseMessageListItem 的类(用于 ImageItem、ImageWithTextItem、VideoItem 等)。
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()
我尝试使用viewHolderType
in解析它,RuntimeTypeAdapterFactory
但由于它不是 的属性BaseMessageListItem
,因此无法解析它。
任何人都有处理这种类型的经验JSON
,请分享任何指示。
解决方案
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()
}
推荐阅读
- reactjs - 从 create-react-app service-worker URL 屏蔽中免除 URL
- angular5 - Angular HttpClient 实际上并没有转换为对象
- docker - 无法使用 Docker 容器上的 TC 规则准确地限制带宽
- asp.net - 尝试返回 SelectListItem 下拉列表的视图模型
- php - 当我在我的 PHP 文件 Twilio 中调用变量时出错发送消息
- vba - 范围类的自动填充方法失败 - 目标字段包含源字段
- c# - asp.net 创建请求作为移动设备
- node.js - 来自车把表单的 POST 请求给出 404
- sql - SQL 根据第三列生成组和子组的等级
- javascript - 需要按钮从值 a 到值 b 前后移动,反之亦然