Kotlin: Kotlin序列化详解
最后更新:2026-08-26
kotlinx.serialization 用编译期插件生成序列化器——Charlie 的 Order ↔ JSON 转换零反射、类型安全、比 Jackson 更快。 一行完成字段映射, 一行配置容错策略。
1. 你将学到
- 编译期插件生成序列化器
- JSON 编解码: /
- 可选字段与默认值: /
- 多格式支持:JSON / ProtoBuf / CBOR / HOCON
- Charlie 实战:Order ↔ JSON + 字段映射 + 容错配置
2. 一个开发者的真实故事
(1) 痛点:反射序列化的运行时炸弹
Bob 用 Jackson 的反射模式序列化 Order 对象。一次重构将 改为 ,JSON 反序列化静默失败(字段名不匹配),500 笔订单数据丢失。
(2) 编译期序列化的解法
KOTLIN
// Jackson: reflection-based, errors at runtime
@JsonAlias("order_id") // Easy to forget
data class Order(val orderId: String, ...)
// kotlinx.serialization: compile-time, errors at compile time
@Serializable
data class Order(
@SerialName("order_id") val id: String, // Compiler verifies!
val total: Double
)
编译期生成序列化器——字段名变更直接导致编译错误,而非运行时数据丢失。
3. @Serializable 与编译期插件
(1) Gradle 配置
KOTLIN
// build.gradle.kts
plugins {
kotlin("jvm") version "1.9.22"
kotlin("plugin.serialization") version "1.9.22" // Serialization compiler plugin
}
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.2")
}
(2) 基本序列化
KOTLIN
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
@Serializable
data class Order(val id: String, val total: Double, val status: String)
// Serialize: Object -> JSON String
val order = Order("ORD-001", 299.99, "CONFIRMED")
val json = Json.encodeToString(order)
// {"id":"ORD-001","total":299.99,"status":"CONFIRMED"}
// Deserialize: JSON String -> Object
val decoded = Json.decodeFromString`<Order>`(json)
// Order(id=ORD-001, total=299.99, status=CONFIRMED)
(3) 反射 vs 编译期序列化对比
| 维度 | Jackson(反射) | kotlinx.serialization |
|---|---|---|
| 机制 | 运行时反射 | 编译期代码生成 |
| 安全性 | 运行时错误 | 编译期错误 |
| 性能 | 较慢 | 更快(无反射开销) |
| ProGuard | 需要保留规则 | 无需 |
| 多平台 | JVM only | JVM/Native/JS |
4. JSON 编解码
(1) Json 配置
KOTLIN
// Default: strict mode
val strictJson = Json // Fails on unknown keys
// Lenient: ignore unknown keys (API versioning)
val lenientJson = Json {
ignoreUnknownKeys = true // Ignore fields not in class
isLenient = true // Accept malformed JSON
encodeDefaults = true // Include fields with default values
prettyPrint = true // Pretty print output
prettyPrintIndent = " " // Indentation
coerceInputValues = true // Use default for null on non-null
}
val json = lenientJson.encodeToString(order)
(2) Json 配置选项
| 选项 | 默认 | 说明 |
|---|---|---|
| false | 忽略 JSON 中类没有的字段 | |
| false | 宽松解析(允许非标准 JSON) | |
| false | 编码默认值字段 | |
| false | 格式化输出 | |
| false | 非空字段遇 null 取默认值 |
5. 字段映射与默认值
(1) @SerialName 字段映射
KOTLIN
@Serializable
data class Order(
@SerialName("order_id") val id: String, // JSON: order_id
@SerialName("order_total") val total: Double, // JSON: order_total
val status: String = "PENDING" // JSON: status (same name)
)
// JSON: {"order_id":"ORD-001","order_total":299.99,"status":"CONFIRMED"}
(2) 可选字段与默认值
KOTLIN
@Serializable
data class Order(
@SerialName("order_id") val id: String,
@SerialName("order_total") val total: Double,
val status: String = "PENDING", // Optional with default
val customer: String? = null, // Nullable optional
@SerialName("tax_rate") val taxRate: Double = 0.08
)
// Missing optional fields use defaults
val json = """{"order_id":"ORD-001","order_total":299.99}"""
val order = Json.decodeFromString`<Order>`(json)
// Order(id=ORD-001, total=299.99, status=PENDING, customer=null, taxRate=0.08)
(3) @Required 强制要求字段
KOTLIN
@Serializable
data class Order(
@Required val id: String, // MUST be present in JSON
@Required val total: Double // MUST be present in JSON
)
// Missing 'id' or 'total' -> SerializationException
6. 多格式支持
KOTLIN
// ProtoBuf
import kotlinx.serialization.protobuf.ProtoBuf
val protoBytes = ProtoBuf.encodeToByteArray(order)
val fromProto = ProtoBuf.decodeFromByteArray`<Order>`(protoBytes)
// CBOR
import kotlinx.serialization.cbor.Cbor
val cborBytes = Cbor.encodeToByteArray(order)
// HOCON (configuration format)
import kotlinx.serialization.hocon.Hocon
(1) 格式对比
| 格式 | 可读性 | 体积 | 速度 | 适用场景 |
|---|---|---|---|---|
| JSON | 高 | 大 | 中 | API 通信 |
| ProtoBuf | 低(二进制) | 小 | 快 | 高性能 RPC |
| CBOR | 低(二进制) | 中 | 快 | IoT/嵌入式 |
| HOCON | 高 | 中 | 中 | 配置文件 |
7. 序列化编解码流程
sequenceDiagram
participant Obj as Order Object
participant Ser as Serializer
participant JSON as JSON String
Note over Obj,Ser: Encode
Obj->>Ser: @Serializable properties
Ser->>JSON: encodeToString()
Note over JSON: {"order_id":"ORD-001",...}
Note over Ser,Obj: Decode
JSON->>Ser: decodeFromString<Order>(json)
Ser->>Obj: @Serializable constructor
Note over Obj: Order(id=ORD-001,...)
8. 完整示例:OrderProcessor JSON 序列化
KOTLIN
// ============================================
// OrderProcessor - JSON Serialization
// Feature: Order <-> JSON with field mapping
// ============================================
import kotlinx.serialization.*
import kotlinx.serialization.json.*
@Serializable
data class Address(
val street: String,
val city: String,
val country: String
)
@Serializable
data class OrderItem(
val sku: String,
val quantity: Int,
@SerialName("unit_price") val unitPrice: Double
) {
val subtotal: Double get() = quantity * unitPrice
}
@Serializable
data class Order(
@SerialName("order_id") val id: String,
@SerialName("order_total") val total: Double,
val status: String = "PENDING",
val customer: String? = null,
val items: List`<OrderItem>` = emptyList(),
val address: Address? = null,
@SerialName("tax_rate") val taxRate: Double = 0.08,
@SerialName("created_at") val createdAt: String = "2026-01-01T00:00:00Z"
)
val orderJson = Json {
ignoreUnknownKeys = true
encodeDefaults = true
prettyPrint = true
prettyPrintIndent = " "
}
fun main() {
// Create order
val order = Order(
id = "ORD-001",
total = 299.99,
status = "CONFIRMED",
customer = "Alice",
items = listOf(
OrderItem("SKU-WIDGET", 3, 9.99),
OrderItem("SKU-GADGET", 1, 149.99)
),
address = Address("123 Main St", "New York", "US"),
createdAt = "2026-07-13T10:30:00Z"
)
// Serialize: Order -> JSON
println("=== Serialize ===")
val jsonString = orderJson.encodeToString(order)
println(jsonString)
// Deserialize: JSON -> Order
println("\n=== Deserialize ===")
val decoded = orderJson.decodeFromString`<Order>`(jsonString)
println("Order: ${decoded.id}, Total: \$${decoded.total} USD")
println("Items: ${decoded.items.map { "${it.sku} x${it.quantity}" }}")
// Handle unknown keys (API versioning)
println("\n=== API Versioning ===")
val jsonWithExtraFields = """
{
"order_id": "ORD-002",
"order_total": 1500.00,
"status": "SHIPPED",
"unknown_field": "this is fine",
"new_api_version": 2
}
""".trimIndent()
val decodedWithExtra = orderJson.decodeFromString`<Order>`(jsonWithExtraFields)
println("Decoded with extra fields: ${decodedWithExtra.id}")
// Minimal JSON (required fields only)
println("\n=== Minimal JSON ===")
val minimalJson = """{"order_id":"ORD-003","order_total":45.50}"""
val minimal = orderJson.decodeFromString`<Order>`(minimalJson)
println("Minimal: ${minimal.id}, Status: ${minimal.status}, Customer: ${minimal.customer}")
}
输出:
TEXT
📖 仅展示
=== Serialize ===
{
"order_id": "ORD-001",
"order_total": 299.99,
"status": "CONFIRMED",
"customer": "Alice",
"items": [
{
"sku": "SKU-WIDGET",
"quantity": 3,
"unit_price": 9.99
},
{
"sku": "SKU-GADGET",
"quantity": 1,
"unit_price": 149.99
}
],
"address": {
"street": "123 Main St",
"city": "New York",
"country": "US"
},
"tax_rate": 0.08,
"created_at": "2026-07-13T10:30:00Z"
}
=== Deserialize ===
Order: ORD-001, Total: $299.99 USD
Items: [SKU-WIDGET x3, SKU-GADGET x1]
=== API Versioning ===
Decoded with extra fields: ORD-002
=== Minimal JSON ===
Minimal: ORD-003, Status: PENDING, Customer: null
❓ 常见问题
Q kotlinx.serialization 可以和 Jackson 一起用吗?
A 可以,但不建议。两者序列化机制不同。迁移时可以用 过渡,新项目推荐直接用 kotlinx.serialization。
Q 密封类可以序列化吗?
A 可以。 密封类会自动添加类型鉴别器(),反序列化时自动选择正确的子类。
Q 如何自定义序列化逻辑?
A 为类指定自定义序列化器 ,或实现 接口手动控制编解码。
Q 序列化支持泛型吗?
A 支持,但需要用 注解泛型类。decodeFromString 需要指定具体类型参数。
Q 为什么需要 compiler plugin?
A Kotlin 的编译期插件在编译时为 类自动生成序列化器代码,避免运行时反射。这是性能和安全的基础。
Q @SerialName 和 @JsonProperty 有什么区别?
A 功能相同(JSON 字段名映射),但 @SerialName 是 kotlinx.serialization 的注解,编译期处理;@JsonProperty 是 Jackson 的注解,运行时反射处理。
📖 小节
-
- 编译期插件自动生成序列化器,零反射
- /
<T>编解码 - 映射 JSON 字段名,与 Kotlin 属性名解耦
- 默认值 = 可选字段, 强制要求字段
- 实现 API 版本兼容
- 多格式支持:JSON / ProtoBuf / CBOR / HOCON
📝 作业
- 基础题(难度⭐):为 添加 ,序列化为 JSON 并反序列化回来。提示:
- 进阶题(难度⭐⭐):用 映射 Order 的字段名(、),配置 处理 API 演进。提示:参考第 5 节
- 挑战题(难度⭐⭐⭐):实现一个自定义序列化器,将 序列化为扁平 JSON(items 展开为 、 格式)。提示:实现