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

如何在Kotlin中结合Reified、泛型与接口实现JSON解码?

问题描述

我创建了一个使用Gson处理JSON序列化的Kotlin项目,核心代码如下:

import com.google.gson.Gson
import com.google.gson.reflect.TypeToken

interface Serializer {
    fun <T> decodeValue(data: String): T?
}

class JsonSerializer : Serializer {
    override fun <T> decodeValue(data: String): T? {
        try {
            val type = object : TypeToken<T>() {}.type
            val gson = Gson()
            return gson.fromJson<T>(data, type)
        } catch (e: Exception) {
            println("Error when parse: ${e.message}")
        }

        return null
    }
}

class Request<T>(val r: T)

inline fun <reified T> callSerializer(json: String): T? {
    val serializer = JsonSerializer()
    val decoded = serializer.decodeValue<Request<T>>(json)
    return decoded?.r
}

fun main() {
    val finalValue = callSerializer<Request<String>>("{\"r\": \"test\"}")
    println("Decoded data is: $finalValue")
}
  • Request类包含一个泛型类型的内部属性r
  • 我尝试将JSON数据{"r": "test"}转换为Request<String>类型,但运行时抛出类型转换异常:
> Task :run FAILED
Exception in thread "main" java.lang.ClassCastException: class com.google.gson.internal.LinkedTreeMap cannot be cast to class Request (com.google.gson.internal.LinkedTreeMap and Request are in unnamed module of loader 'app')
        at MainKt.main(Main.kt:36)
        at MainKt.main(Main.kt)

Gson把JSON解析成了LinkedTreeMap而非Request类,请问该如何解决?

问题原因及解决方法

原因

问题出在Serializer接口的decodeValue方法中,由于Java/Kotlin的类型擦除机制,当你在非具体化泛型函数里使用TypeToken<T>()时,Gson无法获取到Request<T>的具体泛型类型信息,只能把JSON解析成默认的LinkedTreeMap。

解决方法

有两种实用的修复方式:

方法1:让decodeValue支持传入类型令牌

修改Serializer接口和实现类,允许调用方传入具体的TypeToken,让Gson能准确识别目标类型:

import com.google.gson.Gson
import com.google.gson.reflect.TypeToken

interface Serializer {
    fun <T> decodeValue(data: String, typeToken: TypeToken<T>): T?
}

class JsonSerializer : Serializer {
    private val gson = Gson() // 提前初始化Gson,避免重复创建实例

    override fun <T> decodeValue(data: String, typeToken: TypeToken<T>): T? {
        return try {
            gson.fromJson(data, typeToken.type)
        } catch (e: Exception) {
            println("Error when parse: ${e.message}")
            null
        }
    }
}

class Request<T>(val r: T)

inline fun <reified T> callSerializer(json: String): T? {
    val serializer = JsonSerializer()
    // 传入具体的Request<T>类型令牌
    val typeToken = object : TypeToken<Request<T>>() {}
    val decoded = serializer.decodeValue(json, typeToken)
    return decoded?.r
}

fun main() {
    // 泛型参数只需传String,callSerializer已处理Request的包装逻辑
    val finalValue = callSerializer<String>("{\"r\": \"test\"}")
    println("Decoded data is: $finalValue")
}

方法2:简化实现,直接在泛型函数中处理

如果不需要抽象的Serializer接口,可以直接利用reified特性让Gson获取准确类型:

import com.google.gson.Gson
import com.google.gson.reflect.TypeToken

class Request<T>(val r: T)

private val gson = Gson()

inline fun <reified T> callSerializer(json: String): T? {
    val typeToken = object : TypeToken<Request<T>>() {}
    return try {
        gson.fromJson<Request<T>>(json, typeToken.type)?.r
    } catch (e: Exception) {
        println("Error when parse: ${e.message}")
        null
    }
}

fun main() {
    val finalValue = callSerializer<String>("{\"r\": \"test\"}")
    println("Decoded data is: $finalValue")
}

额外说明

  • 修改后的main函数泛型参数从Request<String>改为String,更贴合callSerializer获取内部属性r的语义
  • 提前初始化Gson实例,避免每次解析重复创建对象,提升性能

内容的提问来源于stack exchange,提问作者Paulo Coutinho

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 13:41:01