Android 和通用类型的 Moshi 适配器
Android and Moshi Adapter with generic type
我正尝试在我的 Android 项目中使用 moshi,但我 运行 遇到了一些问题。
这是一个简化的示例 JSON
{
"data": [
{
"label": "May",
"schedule_items": [
{
"type": "event",
"item": {
"foo": "bar",
"some_prop": 1
},
"schedule_item_groups": [
{
"label": "Friday May 4th",
"schedule_items": [
{
"type": "check_in",
"item": {
"a_different_prop": 15
},
"schedule_item_groups": null
},
{
"type": "game",
"item": {
"yet_another_different_prop": 3598
},
"schedule_item_groups": null
}
]
}
]
}
]
}
]
}
如您所见,它是 ScheduleGroups
的列表,在该对象中您有一个标签和 schedule_items
。这是具有 3 个字段的 ScheduleItem
数组:
type
:字符串标签,用于识别这是哪种类型的项目
item
:可以是 class Event
、Game
和 CheckIn
schedule_item_groups
: A ScheduleGroup
这是一个列表,更多 -
ScheduleItems
所以第一个问题是 ScheduleGroup
有一个 ScheduleItems
列表,每个项目都可以有自己的 ScheduleGroup
列表,其中包含更多项目。
第二个问题是 item
字段,它需要被实例化为三个 class 之一:Event
、Game
、CheckIn
.
我已经为此工作了一段时间,到目前为止我一次只能让一个工作,但不能同时工作。
这里是数据 classes(我只包含了 Item
classes 之一):
data class ScheduleGroup(
val label: String,
val schedule_items: List<ScheduleItem<Any>>
)
data class ScheduleItem<out T>(
val type: String,
val schedule_item_groups: List<ScheduleGroup>
val item: T
) {
abstract class Item
}
data class Event(
val some_prop: Int,
val foo: String
) : ScheduleItem.Item()
这就是我让动态通用 class Item
工作的方式:
@FromJson
fun fromJson(map: Map<*, *>): ScheduleItem<Any> {
val moshi: Moshi = Moshi.Builder().build()
val type: String = map["type"] as String
val itemJson = moshi.adapter(Map::class.java).toJson(map["item"] as Map<*, *>)
val item = when (type) {
EventType.EVENT -> moshi.adapter(Event::class.java).fromJson(itemJson)
EventType.GAME -> moshi.adapter(Game::class.java).fromJson(itemJson)
EventType.CHECK_IN, EventType.CHECK_OUT ->
moshi.adapter(CheckIn::class.java).fromJson(itemJson)
else -> throw Error("Unknown type was found $type")
}
val scheduleGroupType = Types.newParameterizedType(List::class.java, ScheduleGroup::class.java)
@Suppress("UNCHECKED_CAST")
val scheduleGroupJson = moshi.adapter<List<ScheduleGroup>>(scheduleGroupType)
.toJson(map["schedule_item_groups"] as List<ScheduleGroup>?)
val list: List<ScheduleGroup>? = moshi
.adapter<List<ScheduleGroup>>(scheduleGroupType).fromJson(scheduleGroupJson)
return ScheduleItem(type, list ?: listOf(), item)
}
它将正确创建正确的 Item
class,但是当我尝试添加 List<ScheduleGroup>
时出现错误,无论我做什么我似乎都无法同时获得上班。
编辑:
我已经更新了代码以显示我用来尝试反序列化 schedule_item_groups
的代码,它是 List<ScheduleGroup>
.
我收到一个错误:(这与我之前收到的错误不同...)
com.squareup.moshi.JsonDataException: java.lang.IllegalArgumentException: Can not set final java.lang.String field com.roomroster.mobile_android.data.api.schedule.models.ScheduleGroup.label to com.squareup.moshi.LinkedHashTreeMap at $.data[0].schedule_items[1]
我不久前就想通了,但我想我可以 post 我所做的,以防将来对任何人有所帮助。
首先,我创建了一个临时中介 class,以在生成通用数据之前保存数据。
data class ScheduleItem<T>(
val date: Date,
val type: String,
var scheduleGroups: List<ScheduleGroup> = listOf(),
var item: T
) {
data class ScheduleItemJson(
val date: Date,
val type: String,
val schedule_item_groups: List<ScheduleGroup>? = listOf(),
val item: Any
)
}
然后在适配器中
@FromJson fun fromJson(item: ScheduleItem.ScheduleItemJson): ScheduleItem<Any> {
val moshi: Moshi = Moshi.Builder().build()
val json = moshi.adapter(Map::class.java).toJson(item.item as Map<*, *>)
return ScheduleItem(
item.date,
item.type,
item.schedule_item_groups ?: listOf(),
when (item.type) {
ItemType.GAME -> moshi.adapter(GameItem::class.java).fromJson(json)
ItemType.EVENT -> moshi.adapter(EventItem::class.java).fromJson(json)
ItemType.CHECK_IN, ItemType.CHECK_OUT ->
moshi.adapter(ReservationItem::class.java)
.fromJson(json).apply { this!!.type = item.type }
else -> ScheduleItem.NullItem()
}!!
)
}
when
语句创建 <T : Item>
并将其传递给 ScheduleItem
构造函数。
最近遇到了类似的问题,我使用sealed class和JsonAdapter动态构建模型。
我 post 在另一个 post 中编辑了我的答案,参见
我正尝试在我的 Android 项目中使用 moshi,但我 运行 遇到了一些问题。
这是一个简化的示例 JSON
{
"data": [
{
"label": "May",
"schedule_items": [
{
"type": "event",
"item": {
"foo": "bar",
"some_prop": 1
},
"schedule_item_groups": [
{
"label": "Friday May 4th",
"schedule_items": [
{
"type": "check_in",
"item": {
"a_different_prop": 15
},
"schedule_item_groups": null
},
{
"type": "game",
"item": {
"yet_another_different_prop": 3598
},
"schedule_item_groups": null
}
]
}
]
}
]
}
]
}
如您所见,它是 ScheduleGroups
的列表,在该对象中您有一个标签和 schedule_items
。这是具有 3 个字段的 ScheduleItem
数组:
type
:字符串标签,用于识别这是哪种类型的项目item
:可以是 classEvent
、Game
和CheckIn
schedule_item_groups
: AScheduleGroup
这是一个列表,更多 -ScheduleItems
所以第一个问题是 ScheduleGroup
有一个 ScheduleItems
列表,每个项目都可以有自己的 ScheduleGroup
列表,其中包含更多项目。
第二个问题是 item
字段,它需要被实例化为三个 class 之一:Event
、Game
、CheckIn
.
我已经为此工作了一段时间,到目前为止我一次只能让一个工作,但不能同时工作。
这里是数据 classes(我只包含了 Item
classes 之一):
data class ScheduleGroup(
val label: String,
val schedule_items: List<ScheduleItem<Any>>
)
data class ScheduleItem<out T>(
val type: String,
val schedule_item_groups: List<ScheduleGroup>
val item: T
) {
abstract class Item
}
data class Event(
val some_prop: Int,
val foo: String
) : ScheduleItem.Item()
这就是我让动态通用 class Item
工作的方式:
@FromJson
fun fromJson(map: Map<*, *>): ScheduleItem<Any> {
val moshi: Moshi = Moshi.Builder().build()
val type: String = map["type"] as String
val itemJson = moshi.adapter(Map::class.java).toJson(map["item"] as Map<*, *>)
val item = when (type) {
EventType.EVENT -> moshi.adapter(Event::class.java).fromJson(itemJson)
EventType.GAME -> moshi.adapter(Game::class.java).fromJson(itemJson)
EventType.CHECK_IN, EventType.CHECK_OUT ->
moshi.adapter(CheckIn::class.java).fromJson(itemJson)
else -> throw Error("Unknown type was found $type")
}
val scheduleGroupType = Types.newParameterizedType(List::class.java, ScheduleGroup::class.java)
@Suppress("UNCHECKED_CAST")
val scheduleGroupJson = moshi.adapter<List<ScheduleGroup>>(scheduleGroupType)
.toJson(map["schedule_item_groups"] as List<ScheduleGroup>?)
val list: List<ScheduleGroup>? = moshi
.adapter<List<ScheduleGroup>>(scheduleGroupType).fromJson(scheduleGroupJson)
return ScheduleItem(type, list ?: listOf(), item)
}
它将正确创建正确的 Item
class,但是当我尝试添加 List<ScheduleGroup>
时出现错误,无论我做什么我似乎都无法同时获得上班。
编辑:
我已经更新了代码以显示我用来尝试反序列化 schedule_item_groups
的代码,它是 List<ScheduleGroup>
.
我收到一个错误:(这与我之前收到的错误不同...)
com.squareup.moshi.JsonDataException: java.lang.IllegalArgumentException: Can not set final java.lang.String field com.roomroster.mobile_android.data.api.schedule.models.ScheduleGroup.label to com.squareup.moshi.LinkedHashTreeMap at $.data[0].schedule_items[1]
我不久前就想通了,但我想我可以 post 我所做的,以防将来对任何人有所帮助。
首先,我创建了一个临时中介 class,以在生成通用数据之前保存数据。
data class ScheduleItem<T>(
val date: Date,
val type: String,
var scheduleGroups: List<ScheduleGroup> = listOf(),
var item: T
) {
data class ScheduleItemJson(
val date: Date,
val type: String,
val schedule_item_groups: List<ScheduleGroup>? = listOf(),
val item: Any
)
}
然后在适配器中
@FromJson fun fromJson(item: ScheduleItem.ScheduleItemJson): ScheduleItem<Any> {
val moshi: Moshi = Moshi.Builder().build()
val json = moshi.adapter(Map::class.java).toJson(item.item as Map<*, *>)
return ScheduleItem(
item.date,
item.type,
item.schedule_item_groups ?: listOf(),
when (item.type) {
ItemType.GAME -> moshi.adapter(GameItem::class.java).fromJson(json)
ItemType.EVENT -> moshi.adapter(EventItem::class.java).fromJson(json)
ItemType.CHECK_IN, ItemType.CHECK_OUT ->
moshi.adapter(ReservationItem::class.java)
.fromJson(json).apply { this!!.type = item.type }
else -> ScheduleItem.NullItem()
}!!
)
}
when
语句创建 <T : Item>
并将其传递给 ScheduleItem
构造函数。
最近遇到了类似的问题,我使用sealed class和JsonAdapter动态构建模型。
我 post 在另一个 post 中编辑了我的答案,参见