Kotlinx.serialization 项目集成
Gradle 安装
Android / JVM(Kotlin DSL)
// 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
// 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 行)
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 字符串 → 对象
集合、嵌套、枚举
@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 配置常用选项
val json = Json {ignoreUnknownKeys = true // 后端多给字段也不报错encodeDefaults = true // 输出默认值;关掉可减小体积prettyPrint = false // 日常关;调试可开isLenient = true // 宽松模式,允许非标准 JSONallowTrailingComma = trueexplicitNulls = false // null 字段不主动输出// classDiscriminator = "type" // 多态时的类型标记字段名(见下)// namingStrategy = JsonNamingStrategy.SnakeCase // 字段命名转换(版本要求较新)
}
字段改名、别名、跳过
@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(编译期可知子类):
@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 配置 + 解码:
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,或子类在别处定义),需要注册:
val json = Json {ignoreUnknownKeys = trueclassDiscriminator = "type"serializersModule = SerializersModule {polymorphic(Msg::class) {subclass(TextMsg::class, TextMsg.serializer())subclass(ImageMsg::class, ImageMsg.serializer())}}
}
自定义类型 / 自定义序列化器
dependencies {implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.1")
}
@Serializable
data class Event(val id: String,val at: kotlinx.datetime.Instant // 直接用
)
完全自定义(例如 UUID 或特殊格式)
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
)
错误处理(生产可用模板)
import kotlinx.serialization.SerializationException
import kotlinx.serialization.json.Jsoninline 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
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")
val client = HttpClient(Android) {install(ContentNegotiation) {json(Json { ignoreUnknownKeys = true })}
}
@Serializable data class Todo(val id: Int, val title: String)
val todo: Todo = client.get("https://example.com/todo/1").body()
Retrofit(用 Kotlinx 的 Converter)
implementation("com.jakewharton.retrofit:retrofit2-kotlinx-serialization-converter:1.0.0")
implementation("com.squareup.retrofit2:retrofit:2.11.0")
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。
