02.Kotlin Serialization 属性序列化控制

前言

在 Kotlin Serialization 中,序列化规则决定了类属性的序列化行为。本文将详细介绍属性序列化控制、名称定制、默认值处理和类型安全等核心规则,帮助开发者准确理解数据的序列化过程。

1. 属性序列化控制

1.1 背景字段序列化规则

在 Kotlin Serialization 中,只有具有背景字段(backing field)的属性才会被序列化。

什么是背景字段?

背景字段是 Kotlin 为属性自动生成的内部存储字段。当属性需要存储值时,编译器会创建一个背景字段。具体规则如下:

  • 构造函数参数声明的属性(val/var)有背景字段
  • 类体中声明的可变属性(var)有背景字段
  • 只有自定义 getter 的属性没有背景字段
  • 委托属性没有背景字段(值存储在委托对象中)
kotlin 复制代码
@Serializable
class Student(val name: String) {
    var id: Int = 0
    val domainName: String
        get() = "Student/$name"
    val delegateName by ::name
}

fun main() {
    val data = Student("iKun").apply { id = 10001 }
    println(Json.encodeToString(data))
    // 输出: {"name":"iKun","id":10001}
}

从输出结果可以看出,只有 nameid 属性出现在 JSON 中,而 domainNamedelegateName 属性被忽略了。

1.2 默认值和可选属性

1.2.1 可选属性

当对象的某个属性在输入数据中缺失时,反序列化会失败。解决方法是为属性提供默认值,使该属性自动变为可选。

kotlin 复制代码
@Serializable
data class Student(
    val name: String,
    val language: String = "Chinese",
)

fun main() {
    val json1 = """{"name":"iKun"}"""
    println(Json.decodeFromString<Student>(json1))
    // 输出: Student(name=iKun, language=Chinese)

    val json2 = """{"name":"iKun","language":"English"}"""
    println(Json.decodeFromString<Student>(json2))
    // 输出: Student(name=iKun, language=English)
}

1.2.2 默认值编码策略

默认情况下,等于默认值的属性不会被编码到 JSON 中,这样可以减少数据冗余。

kotlin 复制代码
@Serializable
data class Student(
    val name: String,
    val language: String = "Chinese"
)

fun main() {
    val student1 = Student(name = "iKun")
    println(Json.encodeToString(student1))
    // 输出: {"name":"iKun"}

    val student2 = Student(name = "iKun", language = "English")
    println(Json.encodeToString(student2))
    // 输出: {"name":"iKun","language":"English"}
}

1.2.3 强制编码默认值 - @EncodeDefault

使用 @EncodeDefault 注解可以强制编码默认值,无论格式设置如何。该注解只影响编码过程,不影响解码。

kotlin 复制代码
@Serializable
data class Student(
    val name: String,
    @EncodeDefault
    val language: String = "Chinese"
)

fun main() {
    println(Json.encodeToString(Student(name = "iKun")))
    // 输出: {"name":"iKun","language":"Chinese"}
}

@EncodeDefault 注解可以指定参数 EncodeDefault.Mode.NEVEREncodeDefault.Mode.ALWAYS 强制指定默认值的编码行为。

1.2.4 Json 格式的 encodeDefaults 配置

除了使用 @EncodeDefault 注解控制单个属性外,还可以通过 Json 实例的 encodeDefaults 配置来全局控制默认值的编码策略。

kotlin 复制代码
@Serializable
data class Student(
    val name: String,
    val language: String = "Chinese"
)

fun main() {
    val json = Json { encodeDefaults = true }
    println(json.encodeToString(Student(name = "iKun")))
    // 输出: {"name":"iKun","language":"Chinese"}
}

注意事项:

  • @EncodeDefault 注解的优先级高于 encodeDefaults 配置
  • 这个配置只影响编码过程,不影响解码过程

1.2.5 初始化器调用优化

当可选属性在输入中存在时,对应的初始化器不会被调用,这是为了性能考虑。

kotlin 复制代码
fun default(): String {
    println("获取默认语言.")
    return "Chinese"
}

@Serializable
data class Student(
    val name: String,
    val language: String = default(),
)

fun main() {
    Json.decodeFromString<Student>("""{"name":"iKun","language":"English"}""")
    // 不会打印"获取默认语言."
}

1.3 瞬态属性 - @Transient

使用 @Transient 注解可以将属性从序列化过程中排除。瞬态属性必须提供默认值。

kotlin 复制代码
@Serializable
data class Student(
    val name: String,
    @Transient
    val language: String = "Chinese",
)

fun main() {
    println(Json.encodeToString(Student(name = "iKun")))
    // 输出: {"name":"iKun"}

    println(Json.encodeToString(Student(name = "iKun", language = "English")))
    // 输出: {"name":"iKun"}
}

1.4 必需属性 - @Required

对于有默认值的属性,可以使用 @Required 注解强制要求该属性在序列化格式中必须存在。

kotlin 复制代码
@Serializable
data class Student(
    val name: String,
    @Required
    val language: String = "Chinese",
)

fun main() {
    Json.decodeFromString<Student>("""{"name":"iKun"}""")
    // 运行报错:Caused by: kotlinx.serialization.MissingFieldException: Field 'language' is required for type with serial name 'org.demo.Student', but it was missing
}

2. 序列化名称定制

2.1 自定义字段名称 - @SerialName

默认情况下,序列化使用的字段名称与源代码中的属性名称相同。可以使用 @SerialName 注解来自定义序列化名称。

kotlin 复制代码
@Serializable
data class Student(
    @SerialName("studentName") val name: String,
    val language: String,
)

fun main() {
    val jsonStr = Json.encodeToString(Student(name = "iKun", language = "Chinese"))
    println(jsonStr)
    // 输出: {"studentName":"iKun","language":"Chinese"}

    println(Json.decodeFromString<Student>(jsonStr))
    // 输出: Student(name=iKun, language=Chinese)
}

2.2 枚举类型的序列化

2.2.1 枚举默认序列化

枚举类型天然支持序列化,无需添加 @Serializable 注解。默认情况下,枚举值以字符串形式序列化,使用枚举条目名称作为序列化值。

kotlin 复制代码
enum class Language {
    KOTLIN, PYTHON
}

@Serializable
data class Student(
    val name: String,
    val favoriteLanguage: Language
)

fun main() {
    val student = Student("iKun", Language.KOTLIN)
    println(Json.encodeToString(student))
    // 输出: {"name":"iKun","favoriteLanguage":"KOTLIN"}
    
    val json = """{"name":"iKun","favoriteLanguage":"PYTHON"}"""
    println(Json.decodeFromString<Student>(json))
    // 输出: Student(name=iKun, favoriteLanguage=PYTHON)
}

2.2.2 自定义枚举序列名称

枚举类型也可以使用 @SerialName 自定义序列化名称。使用 @SerialName 的枚举类必须标记为 @Serializable

kotlin 复制代码
@Serializable
enum class Language {
    @SerialName("1") KOTLIN,
    @SerialName("2") PYTHON,
}

@Serializable
data class Student(
    val name: String,
    val favoriteLanguage: Language
)

fun main() {
    val student = Student("iKun", Language.KOTLIN)
    println(Json.encodeToString(student))
    // 输出: {"name":"iKun","favoriteLanguage":"1"}

    val json = """{"name":"iKun","favoriteLanguage":"2"}"""
    println(Json.decodeFromString<Student>(json))
    // 输出: Student(name=iKun, favoriteLanguage=PYTHON)
}

3. 类型安全和数据验证

3.1 构造时数据验证

反序列化过程会调用所有 init 块,因此可以在其中进行数据验证。

kotlin 复制代码
@Serializable
data class Student(
    val name: String,
    val language: String
) {
    init {
        require(name.isNotEmpty() && name.isNotBlank()) { "名称不合法" }
    }
}

fun main() {
    val json = """{"name":"","language":"Chinese"}"""
    Json.decodeFromString<Student>(json)
    // 运行报错: java.lang.IllegalArgumentException: 名称不合法 at org.example.Student.<init>
}

3.2 类型安全保证

Kotlin Serialization 严格执行类型安全,不允许将 null 值赋给非空类型的属性。

kotlin 复制代码
@Serializable
data class Student(
    val name: String,
    val language: String
)

fun main() {
    val json = """{"name":"","language":null}"""
    Json.decodeFromString<Student>(json)
    // 运行报错: kotlinx.serialization.json.internal.JsonDecodingException: Unexpected JSON token at offset 22: Expected string literal but 'null' literal was found
}

3.3 可空属性处理

可空属性会被正常序列化和反序列化:

kotlin 复制代码
@Serializable
class Project(
    val name: String, 
    val renamedTo: String? = null // 可空属性
)

fun main() {
    val data = Project("kotlinx.serialization")
    println(Json.encodeToString(data))
    // 输出: {"name":"kotlinx.serialization"} (null 值不被编码)
    
    val dataWithRename = Project("old-name", "new-name")
    println(Json.encodeToString(dataWithRename))
    // 输出: {"name":"old-name","renamedTo":"new-name"}
}

3.4 构造函数属性要求

@Serializable 注解要求主构造函数的所有参数都必须是属性。如需其他逻辑,可以使用私有主构造函数配合辅助构造函数:

kotlin 复制代码
@Serializable
class Student private constructor(
    val name: String,
    val language: String,
) {
    constructor(input: String) : this(
        name = input.substringBefore('/'),
        language = input.substringBefore('/'),
    )
}

fun main() {
    val json = Json { encodeDefaults = true }
    println(json.encodeToString(Student(input = "iKun/Chinese")))
    // 输出: {"name":"iKun","language":"iKun"}
}

4. 总结与最佳实践

4.1 核心规则回顾

控制类型 实现方式 关键要点
属性选择 背景字段机制 委托属性、计算属性不参与序列化
属性排除 @Transient 必须提供默认值
强制必需 @Required 覆盖默认值的可选性
名称定制 @SerialName 枚举使用时需添加@Serializable
默认值控制 @EncodeDefault / encodeDefaults 注解优先级高于全局配置

4.2 设计限制与应用场景

Kotlin Serialization 专为纯数据序列化设计,具有以下限制:

  • 不支持复杂对象图和循环引用
  • 相同对象的多次引用会被分别序列化
  • 适用于简单的数据传输对象(DTO)

4.3 开发建议

避免常见陷阱

  • 注意委托属性不会被序列化
  • 避免在属性初始化器中依赖副作用
  • 主构造函数参数必须是属性

性能优化

  • 使用@Transient排除不必要的数据
  • 合理配置默认值策略减少传输量
  • 根据场景选择不同的Json配置

掌握这些规则,能够充分发挥 Kotlin Serialization 的优势,构建高效可靠的数据序列化系统。

相关推荐
Cachel wood5 小时前
信息检索、推荐系统模型排序质量指标:AP@K和MAP@K
windows·搜索引擎·json·推荐系统·搜索
tebukaopu14812 小时前
json文件转excel
json·excel
tangweiguo030519871 天前
Kable使用指南:Android BLE开发的现代化解决方案
android·kotlin
yzpyzp1 天前
kotlin的函数前面增加suspend关键字的作用
android·开发语言·kotlin
jiet_h1 天前
Android Kotlin ObjectAnimator 和 ValueAnimator 全面解析
android·开发语言·kotlin
Android技术之家1 天前
Kotlin与Compose:Android开发的现代化变革
android·java·开发语言·kotlin
小孔龙1 天前
01.Kotlin Serialization - 基础用法
kotlin·json
叽哥1 天前
Kotlin学习第 5 课:Kotlin 面向对象编程:类、对象与继承
android·java·kotlin
叽哥1 天前
Kotlin学习第 6 课:Kotlin 集合框架:操作数据的核心工具
android·java·kotlin