引言:为什么需要新的序列化方案?
在 Android / Kotlin 项目中,我们习惯于用 Gson/Moshi 来处理 JSON,但随着业务扩大,我们遇到:
| 问题 | 表现 |
|---|---|
| ❌ 反射慢、包体大 | 冷启动有明显反射开销,尤其 fragment/activity 初始化 |
| ❌ 字段兼容差 | 后端新增字段 → JsonSyntaxException |
| ❌ 多态类难处理 | sealed class 支持不友好 |
| ❌ 配置分散 | 需要在 Model 或 Adapter 注册序列化逻辑 |
Kotlinx.serialization 的出现就是为了解决这些痛点。
Kotlinx.serialization 是什么?
一句话定义:
Kotlin 官方提供的 编译期序列化框架,支持 JSON、ProtoBuf、CBOR,跨 JVM / Android / KMM。
特性总结:
✅ 无反射,编译期生成序列化器
✅ Kotlin-first,支持 data/sealed class
✅ 支持多格式 JSON / Protobuf / CBOR
✅ 可与 Ktor / Retrofit 无缝集成
✅ 更轻量小包体,不需要 keep 反射 class
基本用法(3 行代码)
Kotlin
@Serializable
data class User(val id: Long, val name: String)
val json = Json { ignoreUnknownKeys = true }
val str = json.encodeToString(User(1, "Ling"))
val u = json.decodeFromString<User>(str)
重点:必须加 @Serializable,编译期自动生成序列化器,不用反射。
JSON 配置建议(够用就这个)
Kotlin
val JSONx = Json {
ignoreUnknownKeys = true // 多字段容错
encodeDefaults = false // 不把默认值写入JSON
explicitNulls = false // 不输出 null 字段
isLenient = true // 宽松解析
coerceInputValues = true // 类型不匹配挽救
}
开启
ignoreUnknownKeys=true后,后端新增字段不会炸。
常用注解(70% 的问题靠这些解决)
| 注解 | 功能 |
|---|---|
@SerialName("xxx") |
JSON key 映射 |
@JsonNames("a","b") |
后端字段不统一时兼容多个 |
@Transient |
不序列化某字段 |
@Contextual |
第三方类,如 UUID、Date |
@Serializable(with=...) |
自定义序列化器 |
多态对象(sealed class 最爽)
Kotlin
@Serializable
sealed class Message {
@Serializable @SerialName("text")
data class Text(val content: String): Message()
@Serializable @SerialName("image")
data class Image(val url: String): Message()
}
val json = Json { classDiscriminator = "type" }
输出 JSON:
{"type":"text","content":"hello"}
sealed +
classDiscriminator= 强无敌。
tips:
Kotlinx.serialization 对多态对象(sealed class )支持更好用
自定义序列化器(处理后端不稳定 JSON)
例子:后端返回 "timestamp": "1738918293128"(字符串)
Kotlin
object EpochMilliAsString : KSerializer<Long> {
override val descriptor = PrimitiveSerialDescriptor("Epoch", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: Long) =
encoder.encodeString(value.toString())
override fun deserialize(decoder: Decoder) =
decoder.decodeString().toLong()
}
Retrofit/Ktor 集成(推荐)
Retrofit
Kotlin
val json = Json {
ignoreUnknownKeys = true
// 过渡期若按内容判别:无需 classDiscriminator
// 将来有 type:classDiscriminator = "type"
}
val retrofit = Retrofit.Builder()
.baseUrl("https://api.example.com/")
.addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
.build()
interface Api {
@GET("messages")
suspend fun list(): List<Message>
}
Ktor Client
Kotlin
val client = HttpClient(OkHttp) {
install(ContentNegotiation) { json(Json {
ignoreUnknownKeys = true
// classDiscriminator = "type"
}) }
}
与 Gson 对比总结
| 对比项 | Gson/Moshi | Kotlinx.serialization |
|---|---|---|
| 性能 | ❌ 反射 | ✅ 编译期生成,无反射 |
| sealed class 多态支持 | ❌ 复杂 | ✅ 一行搞定 |
| Kotlin default 值序列化 | ❌ 不支持 | ✅ 原生支持 |
| 包体积 | ❌ 大 | ✅ 小 |
| 跨平台(KMM) | ❌ 不支持 | ✅ KMM 友好 |
项目落地策略(迁移方案)
建议逐模块迁移:
新模块 → Kotlinx.serialization
旧模块 → 可混用 Gson,不强制一次性改完
迁移 checklist:
-
Model 加
@Serializable -
Json 全局单例(避免频繁创建)
-
Retrofit/Ktor 换 converter
-
sealed class 都替换到多态序列化