Gradle 安装
Android / JVM(Kotlin DSL)
Groovy
// app/build.gradle.kts
plugins {
id("org.jetbrains.kotlin.android")
id("org.jetbrains.kotlin.plugin.serialization") // ← 必须
}
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3") // 版本示例
}
Kotlin Multiplatform
Groovy
// build.gradle.kts (KMP 根 module)
plugins {
kotlin("multiplatform")
id("org.jetbrains.kotlin.plugin.serialization")
}
kotlin {
androidTarget()
jvm()
ios()
sourceSets {
val commonMain by getting {
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
}
}
}
}
基础用法(最常用 3 行)
Kotlin
import kotlinx.serialization.*
import kotlinx.serialization.json.*
@Serializable
data class User(
val id: Long,
val name: String,
val email: String? = null, // 可空 & 有默认值
)
val json = Json { ignoreUnknownKeys = true } // 忽略后端多给的字段
val u = User(1, "Alice")
val s: String = json.encodeToString(u) // 对象 → JSON 字符串
val u2: User = json.decodeFromString<User>(s) // JSON 字符串 → 对象
集合、嵌套、枚举
Kotlin
@Serializable
enum class Role { ADMIN, USER, GUEST }
@Serializable
data class Team(
val name: String,
val members: List<User>,
val tags: Set<String> = emptySet(),
val roles: Map<Long, Role> = emptyMap()
)
Json 配置常用选项
Kotlin
val json = Json {
ignoreUnknownKeys = true // 后端多给字段也不报错
encodeDefaults = true // 输出默认值;关掉可减小体积
prettyPrint = false // 日常关;调试可开
isLenient = true // 宽松模式,允许非标准 JSON
allowTrailingComma = true
explicitNulls = false // null 字段不主动输出
// classDiscriminator = "type" // 多态时的类型标记字段名(见下)
// namingStrategy = JsonNamingStrategy.SnakeCase // 字段命名转换(版本要求较新)
}
字段改名、别名、跳过
Kotlin
@Serializable
data class Article(
@SerialName("id") val articleId: String, // 接口字段叫 id,本地想叫 articleId
@SerialName("title") val title: String,
@JsonNames("cover", "thumbnail") val image: String? = null, // 兼容多个可能的字段名
@Transient val cacheOnly: Boolean = false // 仅本地使用,不参与序列化
)
多态 & sealed class(后端返回不同"类型"的统一字段)
推荐 sealed class(编译期可知子类):
Kotlin
@Serializable
sealed class Msg {
abstract val id: String
}
@Serializable
@SerialName("text")
data class TextMsg(override val id: String, val text: String): Msg()
@Serializable
@SerialName("image")
data class ImageMsg(override val id: String, val url: String): Msg()
Json 配置 + 解码:
Kotlin
val json = Json {
classDiscriminator = "type" // 后端会给 {"type":"text", ...}
ignoreUnknownKeys = true
}
val msg: Msg = json.decodeFromString("""{"type":"text","id":"1","text":"hi"}""")
when (msg) {
is TextMsg -> println(msg.text)
is ImageMsg -> println(msg.url)
}
如果是"开放层级"多态(不是 sealed,或子类在别处定义),需要注册:
Kotlin
val json = Json {
ignoreUnknownKeys = true
classDiscriminator = "type"
serializersModule = SerializersModule {
polymorphic(Msg::class) {
subclass(TextMsg::class, TextMsg.serializer())
subclass(ImageMsg::class, ImageMsg.serializer())
}
}
}
自定义类型 / 自定义序列化器
Groovy
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.1")
}
Kotlin
@Serializable
data class Event(
val id: String,
val at: kotlinx.datetime.Instant // 直接用
)
完全自定义(例如 UUID 或特殊格式)
Kotlin
import kotlinx.serialization.*
import kotlinx.serialization.descriptors.*
import kotlinx.serialization.encoding.*
import java.util.UUID
@Serializer(forClass = UUID::class)
object UUIDAsString : KSerializer<UUID> {
override val descriptor: SerialDescriptor =
PrimitiveSerialDescriptor("UUIDAsString", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: UUID) {
encoder.encodeString(value.toString())
}
override fun deserialize(decoder: Decoder): UUID =
UUID.fromString(decoder.decodeString())
}
@Serializable
data class WithUuid(
@Serializable(with = UUIDAsString::class)
val id: UUID
)
错误处理(生产可用模板)
Kotlin
import kotlinx.serialization.SerializationException
import kotlinx.serialization.json.Json
inline fun <reified T> safeDecode(
jsonString: String,
json: Json = Json { ignoreUnknownKeys = true },
onError: (String) -> Unit = {}
): T? {
return try {
json.decodeFromString<T>(jsonString)
} catch (e: SerializationException) {
onError("解析失败: ${e.message}\n片段: ${jsonString.take(300)}")
null
}
}
和 Ktor / Retrofit 集成
Ktor Client
Groovy
implementation("io.ktor:ktor-client-core:2.3.7")
implementation("io.ktor:ktor-client-android:2.3.7")
implementation("io.ktor:ktor-client-content-negotiation:2.3.7")
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.7")
Kotlin
val client = HttpClient(Android) {
install(ContentNegotiation) {
json(Json { ignoreUnknownKeys = true })
}
}
Kotlin
@Serializable data class Todo(val id: Int, val title: String)
val todo: Todo = client.get("https://example.com/todo/1").body()
Retrofit(用 Kotlinx 的 Converter)
Groovy
implementation("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0")
implementation("com.squareup.retrofit2:retrofit:2.11.0")
Kotlin
val json = Json { ignoreUnknownKeys = true }
val retrofit = Retrofit.Builder()
.baseUrl("https://api.example.com/")
.addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
.build()
与 Gson 的关键差异(迁移要点)
-
无反射 :kotlinx 在编译期生成
$$serializer,启动更快、可裁剪。 -
默认值处理 :
encodeDefaults决定是否输出默认值;Gson 默认不输出transient字段,kotlinx 用@Transient。 -
字段名 :用
@SerialName、@JsonNames;或全局namingStrategy。 -
多态更安全 :
sealed + classDiscriminator明确标记类型。 -
忽略未知字段 :记得开
ignoreUnknownKeys = true,避免线上因后端加新字段而崩。
混淆 / 体积
-
一般不需要额外 keep(因编译期生成并显式引用)。
-
如果你自定义了
KSerializer或用了反射式工厂,才可能需要补充 keep。 -
体积优化:关闭
prettyPrint,必要时explicitNulls=false,并按需关闭encodeDefaults。
常见坑与排查
-
忘记加插件 :
id("org.jetbrains.kotlin.plugin.serialization")。 -
字段名对不上 :用
@SerialName或开namingStrategy。 -
接口偶发多字段 :
ignoreUnknownKeys = true。 -
后端大小写不一致 :
JsonNamingStrategy.SnakeCase或@JsonNames(...)做兼容。 -
多态无法解析 :确认
classDiscriminator字段名与后端一致;开放多态要注册SerializersModule。 -
时间类型 :优先
kotlinx-datetime;或写自定义KSerializer。