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

如何将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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 04:18:23