Kotlin JSON 序列化库选型指南:Kotlinx.serialization vs Gson

引言

在 Kotlin 开发中,JSON 序列化是每个开发者都会遇到的基础需求。面对众多的 JSON 处理库,如何选择最适合项目的方案?本文将通过详细对比 Kotlinx.serializationGson,帮助你做出明智的技术选型。

快速概览

特性 Kotlinx.serialization Gson
设计理念 Kotlin 原生,类型安全优先 Java 通用,灵活便捷优先
类型安全 🔒 编译时安全 ⚠️ 运行时类型擦除
空安全 ✅ 完整支持 ❌ 有限支持
默认值支持 ✅ 开箱即用 ❌ 需要额外配置
性能 🚀 编译时代码生成 🐢 反射机制
Kotlin 特性 ✅ 完整支持 ⚠️ 部分支持

核心问题:为什么 Gson 在 Kotlin 中表现不佳?

根本原因:Gson 是为 Java 设计的

java 复制代码
// Java Bean 风格(Gson 期望的)
public class User {
    private String name;
    private int age;
    
    // 空构造函数 - Gson 需要这个!
    public User() {}
    
    // Setter 方法 - Gson 通过这些设置值
    public void setName(String name) { this.name = name; }
    public void setAge(int age) { this.age = age; }
}

Gson 的反射机制是基于 Java Bean 规范的,它无法理解 Kotlin 的现代语言特性。

实际问题演示

问题案例:默认值失效

Kotlin 复制代码
data class QuestionRequest(
    val chatId: Int,
    val question: String,
    val robotId: String = "96"  // ⚠️ 这个默认值在 Gson 中无效
)

// 测试
val json = """{"chatId": 123, "question": "Hello"}"""
val request = Gson().fromJson(json, QuestionRequest::class.java)

println(request.robotId) // 可能输出: null 而不是 "96" 😱

基本用法对比

数据模型定义

Kotlinx.serialization

Kotlin 复制代码
import kotlinx.serialization.Serializable

@Serializable
data class User(
    val name: String,
    val age: Int,
    val email: String? = null,        // 可空类型 + 默认值
    val isActive: Boolean = true,     // 默认值
    @SerialName("created_at")         // 字段名映射
    val createdAt: String
)

Gson

Kotlin 复制代码
import com.google.gson.annotations.SerializedName

// 必须使用普通 class 或配置复杂适配器
class User {
    var name: String = ""
    var age: Int = 0
    var email: String? = null
    var isActive: Boolean = true
    @SerializedName("created_at")
    var createdAt: String = ""
}

序列化与反序列化

Kotlinx.serialization

Kotlin 复制代码
import kotlinx.serialization.json.Json

val user = User("Alice", 30, "alice@example.com")

// 序列化
val jsonString = Json.encodeToString(user)

// 反序列化  
val userFromJson = Json.decodeFromString<User>(jsonString)

// 输出: {"name":"Alice","age":30,"email":"alice@example.com","isActive":true,"created_at":"..."}

Gson

Kotlin 复制代码
import com.google.gson.Gson

val user = User().apply {
    name = "Alice"
    age = 30
    email = "alice@example.com"
}

// 序列化
val gson = Gson()
val jsonString = gson.toJson(user)

// 反序列化
val userFromJson = gson.fromJson(jsonString, User::class.java)

深入特性分析

1. 类型安全:编译时 vs 运行时

Kotlinx.serialization 在编译时生成序列化代码,任何类型不匹配都会在编译阶段被发现:

Kotlin 复制代码
// 编译错误:类型不匹配
val user = Json.decodeFromString<User>("""{"name": "Alice", "age": "30"}""")
// ↑ 编译时报错:期望 Int,实际得到 String

Gson 在运行时才会暴露类型问题:

Kotlin 复制代码
// 运行时异常:ClassCastException
val user = gson.fromJson("""{"name": "Alice", "age": "30"}""", User::class.java)
// ↑ 运行时崩溃:java.lang.ClassCastException

2. 空安全支持

Kotlinx.serialization 完全支持 Kotlin 的空安全特性:

Kotlin 复制代码
@Serializable
data class Project(
    val name: String,
    val description: String? = null  // 明确的空安全
)

// JSON 中缺少 description 时,使用默认值 null
val project = Json.decodeFromString<Project>("""{"name": "Kotlin"}""")
println(project.description) // 输出: null

Gson 的空安全支持有限,可能产生意想不到的 null 值。

3. 默认值处理

Kotlinx.serialization 正确处理 Kotlin 的默认值:

Kotlin 复制代码
@Serializable
data class Settings(
    val theme: String = "light",
    val notifications: Boolean = true
)

// 即使 JSON 中缺少字段,也会使用默认值
val settings = Json.decodeFromString<Settings>("""{}""")
println(settings.theme) // 输出: light

Gson 需要额外配置才能正确处理默认值。

问题:Gson 默认不处理 Kotlin 默认值

Kotlin 复制代码
data class User(
    val name: String,
    val age: Int = 25,          // 默认值
    val country: String = "CN", // 默认值
    val isActive: Boolean = true // 默认值
)

val json = """{"name": "Alice"}"""
val user = Gson().fromJson(json, User::class.java)

// 问题:默认值不生效!
println(user.age)      // 输出: 0 (不是 25)
println(user.country)  // 输出: null (不是 "CN")
println(user.isActive) // 输出: false (不是 true)

性能对比

根据实际基准测试,在典型使用场景下:

  • 序列化性能 :Kotlinx.serialization 比 Gson 快 1.5x - 2x

  • 反序列化性能 :Kotlinx.serialization 比 Gson 快 2x - 3x

  • 内存占用 :Kotlinx.serialization 比 Gson 低 30% - 50%

性能优势主要源于:

  • 编译时代码生成 vs 运行时反射

  • 针对 Kotlin 特性的优化

  • 更少的内存分配

配置和依赖

Gson 配置

Kotlin 复制代码
dependencies {
    implementation("com.google.code.gson:gson:2.10.1")
}

Kotlinx.serialization 配置

1. 项目级配置 (project/build.gradle.kts)
Groovy 复制代码
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
    alias(libs.plugins.android.application) apply false
    alias(libs.plugins.kotlin.android) apply false
    alias(libs.plugins.android.library) apply false
    // 添加 kotlinx-serialization 插件
    alias(libs.plugins.kotlin.serialization) apply false
}
2. 版本目录配置 (gradle/libs.versions.toml)
Groovy 复制代码
[versions]
# Kotlin
kotlin = "1.9.10"
# Android
agp = "8.1.0"
# 依赖库
kotlinx-serialization = "1.6.0"
retrofit = "2.9.0"
kotlinx-coroutines = "1.7.3"

[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
android-library = { id = "com.android.library", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }

[libraries]
# Android
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version = "1.12.0" }
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version = "1.6.1" }
androidx-activity = { group = "androidx.activity", name = "activity-ktx", version = "1.8.0" }

# Kotlinx
kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinx-serialization" }
kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" }

# Network
retrofit = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }
retrofit-kotlinx-serialization = { group = "com.jakewharton.retrofit", name = "retrofit2-kotlinx-serialization-converter", version = "1.0.0" }
okhttp-logging = { group = "com.squareup.okhttp3", name = "logging-interceptor", version = "4.12.0" }

# Lifecycle
lifecycle-viewmodel = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-ktx", version = "2.7.0" }
lifecycle-livedata = { group = "androidx.lifecycle", name = "lifecycle-livedata-ktx", version = "2.7.0" }

[bundles]
android-base = ["androidx-core-ktx", "androidx-appcompat", "androidx-activity"]
kotlinx = ["kotlinx-serialization-json", "kotlinx-coroutines-android"]
lifecycle = ["lifecycle-viewmodel", "lifecycle-livedata"]
3. 模块级配置 (app/build.gradle.kts)
Groovy 复制代码
plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.kotlin.android)
    alias(libs.plugins.kotlin.serialization)  // 应用序列化插件
}

android {
    namespace = "com.example.myapp"
    compileSdk = 34

    defaultConfig {
        applicationId = "com.example.myapp"
        minSdk = 21
        targetSdk = 34
        versionCode = 1
        versionName = "1.0"

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            isMinifyEnabled = false
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }

    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }

    kotlinOptions {
        jvmTarget = "1.8"
    }

    buildFeatures {
        viewBinding = true
        buildConfig = true
    }
}

dependencies {
    // Android 基础包
    implementation(libs.bundles.android.base)
    
    // Kotlinx 序列化和协程
    implementation(libs.bundles.kotlinx)
    
    // 网络请求
    implementation(libs.retrofit)
    implementation(libs.retrofit.kotlinx.serialization)
    implementation(libs.okhttp.logging)
    
    // Lifecycle
    implementation(libs.bundles.lifecycle)
    
    // 测试
    testImplementation(libs.junit)
    androidTestImplementation(libs.androidx.test.ext.junit)
    androidTestImplementation(libs.androidx.test.espresso.core)
}
4.验证配置是否成功
创建测试类
Kotlin 复制代码
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json

@Serializable  // 如果这行不报错,说明配置成功!
data class TestModel(
    val id: Int,
    val name: String,
    val description: String = "default"
)

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        testKotlinxSerialization()
    }
    
    private fun testKotlinxSerialization() {
        val testData = TestModel(1, "Test")
        val json = Json.encodeToString(testData)
        Log.d("SerializationTest", "JSON: $json")
        // 应该输出: {"id":1,"name":"Test","description":"default"}
    }
}
配置 Retrofit 转换器
Kotlin 复制代码
import kotlinx.serialization.json.Json
import okhttp3.MediaType.Companion.toMediaType
import retrofit2.Retrofit
import retrofit2.converter.kotlinx.serialization.asConverterFactory

object NetworkClient {
    private val json = Json {
        ignoreUnknownKeys = true
        isLenient = true
    }
    
    private const val BASE_URL = "https://api.example.com/"
    
    val instance: Retrofit by lazy {
        Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(json.asConverterFactory("application/json".toMediaType()))
            .build()
    }
}

实际项目选型建议

选择 Kotlinx.serialization 当:

新开始的 Kotlin 项目 - 享受原生支持的优势

对性能要求高的应用 - 移动端或高并发服务端

需要类型安全的场景 - 大型项目,维护性重要

纯 Kotlin 代码库 - 充分利用 Kotlin 特性

Kotlin 复制代码
// 典型使用场景
@Serializable
data class ApiResponse<T>(
    val code: Int,
    val message: String = "success",
    val data: T? = null
)

suspend fun <T> fetchData(): ApiResponse<T> {
    val response = httpClient.get<String>("api/endpoint")
    return Json.decodeFromString(response)
}

选择 Gson 当:

Java/Kotlin 混合项目 - 更好的兼容性

快速原型开发 - 零配置上手

处理动态 JSON 结构 - 灵活的运行时处理

已有 Gson 依赖的项目 - 避免引入新依赖

Kotlin 复制代码
// Gson 在处理动态结构时的优势
val dynamicJson = """
    {
        "type": "user",
        "data": {"name": "Alice", "age": 30}
    }
"""

val jsonObject = JsonParser.parseString(dynamicJson).asJsonObject
val type = jsonObject.get("type").asString

when (type) {
    "user" -> gson.fromJson(jsonObject.get("data"), User::class.java)
    "product" -> gson.fromJson(jsonObject.get("data"), Product::class.java)
}

迁移策略 (从 Gson 迁移到 Kotlinx.serialization)

第1步:项目级配置

1. 项目级 build.gradle.kts 添加插件

Groovy 复制代码
// project/build.gradle.kts
plugins {
    alias(libs.plugins.android.application) apply false
    alias(libs.plugins.kotlin.android) apply false  
    alias(libs.plugins.android.library) apply false
    // 添加 kotlinx-serialization 插件
    alias(libs.plugins.kotlin.serialization) apply false
}

2. 版本目录配置 (gradle/libs.versions.toml)

Groovy 复制代码
[versions]
kotlin = "1.9.10"
kotlinx-serialization = "1.6.0"

[plugins]
kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }

[libraries]
kotlinx-serialization-json = { group = "org.jetbrains.kotlinx", name = "kotlinx-serialization-json", version.ref = "kotlinx-serialization" }

第2步:模块级配置

app/build.gradle.kts

Groovy 复制代码
plugins {
    alias(libs.plugins.android.application)
    alias(libs.plugins.kotlin.android)
    alias(libs.plugins.kotlin.serialization)  // 应用序列化插件
}

android {
    // 你的现有配置保持不变
    namespace = "com.example.myapp"
    compileSdk = 34
    // ... 其他配置
}

dependencies {
    // 保持现有的 Gson
    implementation("com.google.code.gson:gson:2.10.1")
    
    // 新增 Kotlinx.serialization
    implementation(libs.kotlinx.serialization.json)
    
    // 其他现有依赖...
    implementation("androidx.core:core-ktx:1.12.0")
    implementation("androidx.appcompat:appcompat:1.6.1")
}

第3步:验证配置

同步项目后创建测试类验证

Kotlin 复制代码
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json

// 测试数据类
@Serializable
data class MigrationTest(
    val id: Int,
    val name: String,
    val description: String = "default"
)

object MigrationValidator {
    fun testBasicFunctionality() {
        // 测试 Kotlinx.serialization 基本功能
        val testData = MigrationTest(1, "Test")
        val json = Json.encodeToString(testData)
        println("Kotlinx Serialization Test: $json")
        
        // 测试 Gson 仍然工作
        val gson = com.google.gson.Gson()
        val gsonJson = gson.toJson(testData)
        println("Gson Test: $gsonJson")
    }
}

第4步:渐进迁移 - 开始逐步迁移数据类和业务逻辑

结束。

相关推荐
newchenxf2 小时前
AndroidStudio版本和AGP版本和gradle版本以及kotlin gradle plugin版本关系梳理 2025
android·开发语言·kotlin
猫头虎2 小时前
Rust评测案例:Rust、Java、Python、Go、C++ 实现五大排序算法的执行时间效率比较(基于 OnlineGDB 平台)
java·开发语言·c++·python·golang·rust·排序算法
milanyangbo2 小时前
从局部性原理到一致性模型:深入剖析缓存设计的核心权衡
开发语言·后端·缓存·架构
ftpeak2 小时前
Rust 嵌入式开发的经验之谈
开发语言·后端·rust
lly2024063 小时前
Node.js 多进程
开发语言
曹绍华3 小时前
kotlin扩展函数是如何实现的
android·开发语言·kotlin
上去我就QWER3 小时前
Qt中的QShortcut:高效键盘快捷方式开发指南
开发语言·c++·qt
QT 小鲜肉5 小时前
【C++基础与提高】第二章:C++数据类型系统——构建程序的基础砖石
开发语言·c++·笔记
lsx2024066 小时前
HTML5 新元素
开发语言