使用Gson解析JSON时初始化Kotlin数据类非构造属性
Gson解析Kotlin数据类:非构造属性为null的解决方法
问题描述
我正在开发一个项目,需要使用Gson库实现Kotlin数据类与JSON之间的序列化/反序列化。我的Request类代码如下:
data class Request(val parent_identifier: String?=null, val identifier: String, val display_name: String, val bssid: String, val company_address: LocationAddress, val minimum_match_criteria: Boolean=false, val public_ip:String, val private_ip:String, val network_switches: List<ZoomNetworkSwitch>): HasUid { override val uid: String get() = display_name val country = company_address.country val address_line1 = company_address.address_line1 val address_line2 = company_address.address_line2 val city = company_address.city val state_code = company_address.state_code val zip = company_address.zip }
注意其中基于嵌套company_address的访问器属性。当调用gson().fromJson()方法解析以下JSON字符串时:
{ "display_name": "7th St Café", "private_ip": "111.0.0.0/16", "public_ip": "123.0.0.0/16", "identifier": "CAFE", "company_address": { "address_line1": "420 S 7th St", "address_line2": "7th St Café", "city": "Lewisburg", "country": "US", "state_code": " PA", "zip": " 17837" }, "network_switches": [ { "mac_address": "00:bd:9e:ff:27:00" } ] }
得到的对象中构造属性已正常初始化,但所有非构造属性(country、address_line1等)均为null。需要如何操作才能让Gson正确初始化该类?
解决方案
Gson解析Kotlin数据类时,仅会初始化主构造函数中声明的属性。你定义的country、address_line1这类属于类体中的派生属性,Gson不会自动为其赋值。可通过以下几种方式解决:
1. 将派生属性改为委托属性(推荐)
将这些属性改为从company_address委托读取,Gson初始化company_address后,这些属性会自动获取对应值:
data class Request(val parent_identifier: String?=null, val identifier: String, val display_name: String, val bssid: String, val company_address: LocationAddress, val minimum_match_criteria: Boolean=false, val public_ip:String, val private_ip:String, val network_switches: List<ZoomNetworkSwitch>): HasUid { override val uid: String get() = display_name val country: String by company_address::country val address_line1: String by company_address::address_line1 val address_line2: String? by company_address::address_line2 val city: String by company_address::city val state_code: String by company_address::state_code val zip: String by company_address::zip }
这种方式保持代码简洁,且属性值会随company_address的变化自动更新,同时保留了数据类的不可变性。
2. 自定义Gson TypeAdapter
若需要复杂逻辑,可为Request编写自定义TypeAdapter,在解析完成后手动为派生属性赋值:
class RequestTypeAdapter : TypeAdapter<Request>() { private val gson = Gson() private val delegateAdapter = gson.getDelegateAdapter(this, TypeToken.get(Request::class.java)) override fun write(out: JsonWriter, value: Request) { delegateAdapter.write(out, value) } override fun read(`in`: JsonReader): Request { val request = delegateAdapter.read(`in`) // 注意:需先将派生属性改为var才能赋值 request.apply { // 手动赋值派生属性 // country = company_address.country // ... 其他属性同理 } return request } }
注册TypeAdapter到Gson:
val gson = GsonBuilder() .registerTypeAdapter(Request::class.java, RequestTypeAdapter()) .create()
此方法需要修改派生属性为var,破坏了数据类的不可变性,因此仅在特殊场景下使用。
3. 直接使用嵌套属性
若派生属性仅为方便访问,可直接在代码中通过request.company_address.country替代request.country,省去定义派生属性的步骤,从根源避免问题。
内容的提问来源于stack exchange,提问作者pbuchheit
相关产品推荐
相关产品推荐

