Reputation: 665
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()
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()
I tried parsing it using viewHolderType
in RuntimeTypeAdapterFactory
but since it's not a property of BaseMessageListItem
, it is not able to parse it.
Any one has any experience dealing with this type of JSON
, please do share any pointers.
Upvotes: 1
Views: 343
Reputation: 1787
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()
}
Upvotes: 1
Reputation: 2318
I might be understanding you wrong, but I would like to suggest a different approach. I am assuming you would like to assign to get a ViewHolder type directly from what you get in your API response.
There are two approaches I would like to suggest:
viewHolderType
from a String to an Int so as you can be clear with your mapping and then you can directly compare it.viewHolderType
it receives which would be something of as follows.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
}
}
Upvotes: 0