如何将Kotlin数据类中的计算属性进行序列化?
Kotlinx Serialization:让计算属性被JSON包含的解决方法
问题说明
你定义了如下带计算属性type的序列化数据类:
import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json @Serializable data class Mcve ( val account: String ) { @SerialName("type") val type = if (account.startsWith("1")) "preferred" else "regular" } fun main() { println(Json.encodeToString(Mcve.serializer(), Mcve("12345"))) }
但序列化后仅输出{"account":"12345"},type字段被忽略;即使改用lazy代理,结果依然相同。
这是因为Kotlinx Serialization默认只序列化主构造函数中的属性,类体中定义的属性(包括计算属性、lazy代理属性)不会被自动纳入序列化逻辑。
解决方法
方法1:将计算属性移至主构造函数(推荐)
把type作为主构造函数参数,用默认值实现计算逻辑,这样序列化器会自动识别并包含该字段:
@Serializable data class Mcve( val account: String, @SerialName("type") val type: String = if (account.startsWith("1")) "preferred" else "regular" ) fun main() { // 创建实例时只需传入account,type会自动计算 println(Json.encodeToString(Mcve.serializer(), Mcve("12345"))) // 输出:{"account":"12345","type":"preferred"} }
如果希望禁止外部手动传入type,可以用私有主构造函数+次级构造函数的方式:
@Serializable data class Mcve private constructor( val account: String, @SerialName("type") val type: String ) { constructor(account: String) : this(account, if (account.startsWith("1")) "preferred" else "regular") }
方法2:自定义序列化器
如果不想修改主构造函数,可以自定义序列化器,手动处理类体中的type字段:
import kotlinx.serialization.* import kotlinx.serialization.json.Json @Serializable(with = McveSerializer::class) data class Mcve(val account: String) { @SerialName("type") val type = if (account.startsWith("1")) "preferred" else "regular" } object McveSerializer : KSerializer<Mcve> { override val descriptor: SerialDescriptor = buildClassSerialDescriptor("Mcve") { element("account", String.serializer().descriptor) element("type", String.serializer().descriptor) } override fun serialize(encoder: Encoder, value: Mcve) { encoder.encodeStructure(descriptor) { encodeStringElement(descriptor, 0, value.account) encodeStringElement(descriptor, 1, value.type) } } override fun deserialize(decoder: Decoder): Mcve { return decoder.decodeStructure(descriptor) { var account = "" while (true) { when (val index = decodeElementIndex(descriptor)) { 0 -> account = decodeStringElement(descriptor, 0) CompositeDecoder.DECODE_DONE -> break else -> throw SerializationException("未知索引:$index") } } Mcve(account) } } } fun main() { println(Json.encodeToString(McveSerializer, Mcve("12345"))) // 输出:{"account":"12345","type":"preferred"} }
这种方式更灵活,但代码量较大,适合复杂场景。
内容的提问来源于stack exchange,提问作者Robert
相关产品推荐
相关产品推荐

