如何基于枚举属性反序列化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
相关产品推荐
相关产品推荐

