Moshi自定义类型适配器传值与多类型数据反序列化问题
解决Moshi根据data_type动态反序列化data字段的问题
我之前也遇到过类似的场景,用Moshi处理这种依赖顶层字段决定子类型的JSON结构,确实需要自定义类型适配器工厂来实现动态解析。下面是一套完整的可行方案:
1. 定义数据模型
首先我们需要定义顶层响应类,以及对应不同data_type的密封类(或父类)来封装data字段:
// 顶层Socket响应类 data class SocketResponse( val data_type: String, val data_id: String, val data: BaseData ) // 所有data类型的父类,用密封类可以更安全地处理类型分支 sealed class BaseData // 对应data_type="user"的具体数据类 data class UserData(val user_id: String, val username: String) : BaseData() // 对应data_type="order"的具体数据类 data class OrderData(val order_no: String, val total_amount: Double) : BaseData()
2. 自定义类型适配器工厂
核心逻辑在这里:我们需要创建一个JsonAdapter.Factory,它会先读取data_type字段的值,然后根据这个值选择对应的适配器来解析data对象:
class BaseDataAdapterFactory : JsonAdapter.Factory { override fun create( type: Type, annotations: MutableSet<out Annotation>, moshi: Moshi ): JsonAdapter<*>? { // 只处理BaseData类型的适配请求 if (type != BaseData::class.java) return null // 预先创建好各个子类型的适配器 val userDataAdapter = moshi.adapter(UserData::class.java) val orderDataAdapter = moshi.adapter(OrderData::class.java) // 用Map维护data_type和适配器的映射,避免过多when分支(适合类型较多的场景) val typeAdapterMap = mapOf( "user" to userDataAdapter, "order" to orderDataAdapter ) return object : JsonAdapter<BaseData>() { override fun fromJson(reader: JsonReader): BaseData? { // 先把整个顶层JSON读取为Map,这样能同时拿到data_type和data字段 val topLevelJson = reader.readJsonValue() as Map<String, Any?> val dataType = topLevelJson["data_type"] as? String ?: throw IllegalArgumentException("Missing or invalid data_type field") val dataJson = topLevelJson["data"] as? Map<String, Any?> ?: throw IllegalArgumentException("Missing or invalid data field") // 根据data_type获取对应的适配器,解析data字段 return typeAdapterMap[dataType]?.fromJsonValue(dataJson) ?: throw IllegalArgumentException("Unsupported data_type: $dataType") } override fun toJson(writer: JsonWriter, value: BaseData?) { // 如果需要序列化(发送数据给服务器),这里也可以反向处理 if (value == null) { writer.nullValue() return } writer.beginObject() when (value) { is UserData -> { writer.name("data_type").value("user") writer.name("data").value(userDataAdapter.toJsonValue(value)) } is OrderData -> { writer.name("data_type").value("order") writer.name("data").value(orderDataAdapter.toJsonValue(value)) } } // 这里注意要写入data_id,或者根据实际场景从外部传入 writer.name("data_id").value("") writer.endObject() } } } }
3. 注册适配器并使用
最后把自定义的工厂注册到Moshi实例中,就可以正常解析Socket返回的JSON了:
// 构建带有自定义适配器的Moshi实例 val moshi = Moshi.Builder() .add(BaseDataAdapterFactory()) .build() // 创建顶层响应类的适配器 val socketResponseAdapter = moshi.adapter(SocketResponse::class.java) // 解析Socket接收到的JSON字符串 val jsonString = """{"data_type":"user","data_id":"123","data":{"user_id":"456","username":"joel"}}""" val response = socketResponseAdapter.fromJson(jsonString) // 处理解析后的对象 when (response?.data) { is UserData -> println("Received user data: ${response.data.username}") is OrderData -> println("Received order data: ${response.data.order_no}") null -> println("Failed to parse response") }
额外注意事项
- 如果你的
data_type对应的子类型很多,建议用Map来维护映射关系,代码会更简洁易维护; - 要做好异常处理,比如
data_type不存在、data字段格式错误等情况; - 如果是Java项目,实现逻辑类似,只是需要用
TypeAdapter而非Kotlin的JsonAdapter,API略有差异。
内容的提问来源于stack exchange,提问作者Joel Shea
相关产品推荐
相关产品推荐

