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

Gson生成Kotlin数据类时init块未执行导致字段为空求助

问题原因分析

Gson的默认反序列化机制并不会调用你的类的构造函数(包括主构造器和次级构造器),也不会执行init块。它是通过反射直接创建对象实例(甚至会绕过构造函数,比如用Unsafe API),然后逐个给类的字段赋值。这就是为什么你的init块从来没执行,query和search始终为空的原因——不管是data类还是普通类,只要Gson用默认方式解析,就不会触发这些初始化逻辑。

解决方案

下面提供几种可行的解决办法,你可以根据自己的需求选择:

方法1:让Gson调用带注解的构造器(最简单)

给你的类的主构造器加上@JsonConstructor注解,这样Gson就会主动调用这个构造器来创建对象,init块也就会被执行了。注意如果是普通类(去掉data关键字),这个方法同样有效:

import com.google.gson.annotations.JsonConstructor
import org.json.JSONObject

class Configuration @JsonConstructor constructor(
    val groupId: Int,
    val key: String,
    val source: String
) {
    val query: String
    val search: String

    init {
        val obj = JSONObject(key)
        query = obj.getString("q")
        search = obj.getString("search")
    }
}

这样当你用Gson解析JSON时,就会先调用带@JsonConstructor的构造器,然后执行init块里的解析逻辑,正确初始化query和search。

方法2:自定义JsonDeserializer(最灵活)

如果需要更复杂的解析逻辑,或者不想依赖init块,你可以实现Gson的JsonDeserializer接口,手动控制整个解析过程:

第一步:修改Configuration类

把query和search放到主构造器中,让它成为一个标准的数据类(或者普通类):

data class Configuration(
    val groupId: Int,
    val key: String,
    val source: String,
    val query: String,
    val search: String
)

第二步:实现自定义解析器

import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import com.google.gson.JsonParseException
import org.json.JSONObject
import java.lang.reflect.Type

class ConfigurationDeserializer : JsonDeserializer<Configuration> {
    override fun deserialize(
        json: JsonElement?,
        typeOfT: Type?,
        context: JsonDeserializationContext?
    ): Configuration {
        val jsonObject = json?.asJsonObject ?: throw JsonParseException("Invalid Configuration JSON")
        
        // 解析基础字段
        val groupId = jsonObject.get("groupId").asInt
        val keyStr = jsonObject.get("key").asString
        val source = jsonObject.get("source").asString
        
        // 解析key字段中的query和search
        val keyJson = JSONObject(keyStr)
        val query = keyJson.getString("q")
        val search = keyJson.getString("search")
        
        // 创建并返回Configuration对象
        return Configuration(groupId, keyStr, source, query, search)
    }
}

第三步:注册解析器到Gson

创建Gson实例时注册这个自定义解析器:

import com.google.gson.GsonBuilder

val gson = GsonBuilder()
    .registerTypeAdapter(Configuration::class.java, ConfigurationDeserializer())
    .create()

// 现在用这个gson实例解析就会生效
val config = gson.fromJson(jsonString, Configuration::class.java)

这种方法的优势是完全掌控解析逻辑,适合后续需要扩展的场景。

方法3:使用lateinit var和@JsonIgnore(折中方案)

如果你想保留data类的特性,也可以把query和search声明为lateinit var,并加上@JsonIgnore让Gson忽略它们,然后在key的自定义setter中完成解析(注意data类的val字段不能有setter,所以需要把key改成var):

import com.google.gson.annotations.JsonIgnore
import org.json.JSONObject

data class Configuration(
    val groupId: Int,
    var key: String,
    val source: String
) {
    @JsonIgnore
    lateinit var query: String
    
    @JsonIgnore
    lateinit var search: String

    // 自定义key的setter
    fun setKey(value: String) {
        key = value
        val obj = JSONObject(value)
        query = obj.getString("q")
        search = obj.getString("search")
    }
}

不过这种方法需要注意,当Gson给key赋值时,默认不会调用自定义的setKey方法,你需要额外配置Gson来启用setter调用,或者在解析完成后手动调用一次setKey。所以相对来说,前两种方法更可靠。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 03:50:15