Kotlin: Kotlin序列化详解

最后更新:2026-08-26

kotlinx.serialization 用编译期插件生成序列化器——Charlie 的 Order ↔ JSON 转换零反射、类型安全、比 Jackson 更快。 一行完成字段映射, 一行配置容错策略。

1. 你将学到


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. 序列化编解码流程

100%
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 的注解,运行时反射处理。

📖 小节


📝 作业

  1. 基础题(难度⭐):为 添加 ,序列化为 JSON 并反序列化回来。提示:
  2. 进阶题(难度⭐⭐):用 映射 Order 的字段名(、),配置 处理 API 演进。提示:参考第 5 节
  3. 挑战题(难度⭐⭐⭐):实现一个自定义序列化器,将 序列化为扁平 JSON(items 展开为 、 格式)。提示:实现

← 上一课 | 下一课 →

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏