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

