You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 07:44:59