Android项目中泛型类型的Moshi Adapter使用问题
解决Android中Moshi泛型多态解析的问题
嘿,我刚好之前处理过类似的Moshi解析场景,给你一套完整的解决方案!你的核心问题是ScheduleItem里的type字段用来标识item的具体类型(Event/Game/CheckIn),而且还有嵌套的schedule_item_groups,这种泛型多态场景Moshi默认没法自动处理,得针对性适配。
1. 先明确数据类结构
首先确保你的数据类定义清晰,这里我用Kotlin示例:
// 外层分组 data class ScheduleGroups( val label: String, val schedule_items: List<ScheduleItem> ) // 单个日程项(先使用Any接收多态的item,后续优化类型安全) data class ScheduleItem( val type: String, val item: Any, val schedule_item_groups: List<ScheduleGroups>? = null ) // 具体的item类型 data class Event(val eventId: String, val name: String) data class Game(val gameId: String, val score: Int) data class CheckIn(val checkInId: String, val time: String)
2. 自定义ScheduleItem的JsonAdapter
因为type字段属于ScheduleItem本身,而非item对象内部,Moshi自带的PolymorphicJsonAdapterFactory没法直接处理这种父对象存判别字段的场景,所以我们要手动写Adapter:
class ScheduleItemAdapter(private val moshi: Moshi) : JsonAdapter<ScheduleItem>() { // 提前初始化各类型的Adapter private val eventAdapter = moshi.adapter(Event::class.java) private val gameAdapter = moshi.adapter(Game::class.java) private val checkInAdapter = moshi.adapter(CheckIn::class.java) private val groupsAdapter = moshi.adapter<List<ScheduleGroups>>() override fun fromJson(reader: JsonReader): ScheduleItem? { reader.beginObject() var type: String? = null var item: Any? = null var groups: List<ScheduleGroups>? = null while (reader.hasNext()) { when (reader.nextName()) { "type" -> type = reader.nextString() "item" -> { type?.let { item = when (it) { "event" -> eventAdapter.fromJson(reader) "game" -> gameAdapter.fromJson(reader) "checkin" -> checkInAdapter.fromJson(reader) else -> throw JsonDataException("未知的item类型: $it") } } ?: throw JsonDataException("item字段前必须先指定type") } "schedule_item_groups" -> groups = groupsAdapter.fromJson(reader) else -> reader.skipValue() // 跳过未知字段 } } reader.endObject() return ScheduleItem( type = requireNotNull(type) { "type字段不能为空" }, item = requireNotNull(item) { "type为$type时,item字段不能为空" }, schedule_item_groups = groups ) } override fun toJson(writer: JsonWriter, value: ScheduleItem?) { if (value == null) { writer.nullValue() return } writer.beginObject() writer.name("type").value(value.type) writer.name("item") // 根据item实际类型序列化 when (value.item) { is Event -> eventAdapter.toJson(writer, value.item) is Game -> gameAdapter.toJson(writer, value.item) is CheckIn -> checkInAdapter.toJson(writer, value.item) else -> throw JsonDataException("无法识别的item类型: ${value.item::class.java}") } // 序列化嵌套分组(如果存在) value.schedule_item_groups?.let { writer.name("schedule_item_groups") groupsAdapter.toJson(writer, it) } writer.endObject() } }
3. 注册Adapter并使用Moshi
创建Moshi实例时,把自定义的Adapter注册进去:
val moshi = Moshi.Builder() .add(ScheduleItemAdapter::class.java) .build() // 解析JSON示例 val scheduleAdapter = moshi.adapter<List<ScheduleGroups>>() val scheduleList = scheduleAdapter.fromJson(你的JSON字符串)
4. 优化:用密封类实现类型安全
如果不想用Any接收item(避免强转),可以把ScheduleItem改成密封类,让类型更安全:
sealed class ScheduleItem { abstract val type: String abstract val schedule_item_groups: List<ScheduleGroups>? data class EventItem( override val type: String = "event", val item: Event, override val schedule_item_groups: List<ScheduleGroups>? = null ) : ScheduleItem() data class GameItem( override val type: String = "game", val item: Game, override val schedule_item_groups: List<ScheduleGroups>? = null ) : ScheduleItem() data class CheckInItem( override val type: String = "checkin", val item: CheckIn, override val schedule_item_groups: List<ScheduleGroups>? = null ) : ScheduleItem() }
对应的Adapter里的fromJson方法也要调整,返回对应的密封类实例,这样使用时就可以用when表达式安全处理,不用强转:
// Adapter的fromJson部分调整 return when (type) { "event" -> ScheduleItem.EventItem( item = requireNotNull(eventAdapter.fromJson(reader)), schedule_item_groups = groups ) "game" -> ScheduleItem.GameItem( item = requireNotNull(gameAdapter.fromJson(reader)), schedule_item_groups = groups ) "checkin" -> ScheduleItem.CheckInItem( item = requireNotNull(checkInAdapter.fromJson(reader)), schedule_item_groups = groups ) else -> throw JsonDataException("未知的item类型: $type") }
使用示例:
scheduleList?.forEach { group -> group.schedule_items.forEach { item -> when (item) { is ScheduleItem.EventItem -> println("事件:${item.item.name}") is ScheduleItem.GameItem -> println("游戏得分:${item.item.score}") is ScheduleItem.CheckInItem -> println("签到时间:${item.item.time}") } } }
内容的提问来源于stack exchange,提问作者jordond
相关产品推荐
相关产品推荐

