Kotlinx.serialization 项目集成

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

常见坑与排查

  1. 忘记加插件id("org.jetbrains.kotlin.plugin.serialization")

  2. 字段名对不上 :用 @SerialName 或开 namingStrategy

  3. 接口偶发多字段ignoreUnknownKeys = true

  4. 后端大小写不一致JsonNamingStrategy.SnakeCase@JsonNames(...) 做兼容。

  5. 多态无法解析 :确认 classDiscriminator 字段名与后端一致;开放多态要注册 SerializersModule

  6. 时间类型 :优先 kotlinx-datetime;或写自定义 KSerializer

相关推荐
Gary Studio9 小时前
Android AIDL HAL工程结构示例
android
学术阿凡提9 小时前
Spring Boot 集成 Fastjson2 完整教程:从入门到避坑
spring boot·安全·json
LIUAWEIO9 小时前
鸽鸽工具网:免费在线工具大全,打开网页即用
人工智能·安全·ai·json
y = xⁿ9 小时前
MySQL八股知识合集
android·mysql·adb
andr_gale10 小时前
04_rc文件语法规则
android·framework·aosp
祖国的好青年11 小时前
VS Code 搭建 React Native 开发环境(Windows 实战指南)
android·windows·react native·react.js
黄林晴11 小时前
警惕!AGP 9.2 别只改版本号,R8 规则与构建链路全线收紧
android·gradle
小米渣的逆袭12 小时前
Android ADB 完全使用指南
android·adb
儿歌八万首12 小时前
Jetpack Compose Canvas 进阶:结合 animateFloatAsState 让自定义图形动起来
android·动画·compose