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

如何基于枚举属性反序列化Kotlin对象?

处理API返回的类型动态变化的Info数据

在Kotlin这类静态类型语言中,无法直接让Info数据类的value字段根据type自动切换类型,但可以通过以下几种方案解决问题,按推荐程度排序:

方案一:使用密封类(Sealed Class)实现类型安全

密封类是处理这种「类型关联值」场景的最优解,能在编译期保证类型完整性,避免运行时类型转换错误。

定义密封类

sealed class Info {
    data class Name(val value: String) : Info()
    data class Height(val value: Int) : Info()
    data class Weight(val value: Double) : Info()
    data class Metric(val value: Boolean) : Info()
}

// 对应枚举类(也可直接在解析时用字符串判断)
enum class InfoType { NAME, HEIGHT, WEIGHT, METRIC }

自定义JSON解析适配器(以Gson为例)

编写类型适配器完成JSON与密封类实例的转换:

class InfoTypeAdapter : TypeAdapter<Info>() {
    override fun write(out: JsonWriter, value: Info) {
        out.beginObject()
        when (value) {
            is Info.Name -> {
                out.name("type").value("name")
                out.name("value").value(value.value)
            }
            is Info.Height -> {
                out.name("type").value("height")
                out.name("value").value(value.value)
            }
            is Info.Weight -> {
                out.name("type").value("weight")
                out.name("value").value(value.value)
            }
            is Info.Metric -> {
                out.name("type").value("metric")
                out.name("value").value(value.value)
            }
        }
        out.endObject()
    }

    override fun read(`in`: JsonReader): Info {
        var type: String? = null
        var valueJson: JsonElement? = null

        `in`.beginObject()
        while (`in`.hasNext()) {
            when (`in`.nextName()) {
                "type" -> type = `in`.nextString()
                "value" -> valueJson = JsonParser.parseReader(`in`)
            }
        }
        `in`.endObject()

        return when (type) {
            "name" -> Info.Name(valueJson?.asString ?: "")
            "height" -> Info.Height(valueJson?.asInt ?: 0)
            "weight" -> Info.Weight(valueJson?.asDouble ?: 0.0)
            "metric" -> Info.Metric(valueJson?.asBoolean ?: false)
            else -> throw IllegalArgumentException("未知的Info类型: $type")
        }
    }
}

注册适配器并解析

val gson = GsonBuilder()
    .registerTypeAdapter(Info::class.java, InfoTypeAdapter())
    .create()

// 解析示例JSON
val json = """[ {"type": "name", "value": "bob"}, {"type": "height", "value": 70}, {"type": "weight", "value": 160.25}, {"type": "metric", "value": false} ]"""
val infoList = gson.fromJson(json, Array<Info>::class.java).toList()

// 使用时的类型判断
for (info in infoList) {
    when (info) {
        is Info.Name -> println("姓名: ${info.value}")
        is Info.Height -> println("身高: ${info.value}")
        is Info.Weight -> println("体重: ${info.value}")
        is Info.Metric -> println("是否使用公制: ${info.value}")
    }
}

方案二:泛型数据类(类型安全性较弱)

如果场景简单,可用泛型定义Info类,但解析列表时需逐个处理元素类型:

data class Info<T>(val type: InfoType, val value: T)

// 解析单个元素示例
val nameJson = """{"type": "name", "value": "bob"}"""
val nameInfo: Info<String> = gson.fromJson(nameJson, object : TypeToken<Info<String>>() {}.type)

val heightJson = """{"type": "height", "value": 70}"""
val heightInfo: Info<Int> = gson.fromJson(heightJson, object : TypeToken<Info<Int>>() {}.type)

这种方式的缺点是解析列表时无法直接得到List<Info<*>>,需手动遍历JSON数组逐个解析,且使用时仍需强制转换泛型类型,容易出错。

方案三:使用Any类型(不推荐)

直接将value定义为Any类型,使用时强制转换,完全没有类型安全保障,极易引发ClassCastException:

data class Info(val type: InfoType, val value: Any)

// 使用时
val info: Info = ...
when (info.type) {
    InfoType.NAME -> val name = info.value as String
    InfoType.HEIGHT -> val height = info.value as Int
    InfoType.WEIGHT -> val weight = info.value as Double
    InfoType.METRIC -> val isMetric = info.value as Boolean
}

内容的提问来源于stack exchange,提问作者daniel-eh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 12:50:58