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步:渐进迁移 - 开始逐步迁移数据类和业务逻辑

结束。

相关推荐
Kapaseker13 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
A0微声z2 天前
Kotlin Multiplatform (KMP) 中使用 Protobuf
kotlin
alexhilton3 天前
使用FunctionGemma进行设备端函数调用
android·kotlin·android jetpack
lhDream3 天前
Kotlin 开发者必看!JetBrains 开源 LLM 框架 Koog 快速上手指南(含示例)
kotlin
RdoZam3 天前
Android-封装基类Activity\Fragment,从0到1记录
android·kotlin
Kapaseker4 天前
研究表明,开发者对Kotlin集合的了解不到 20%
android·kotlin
郑州光合科技余经理4 天前
代码展示:PHP搭建海外版外卖系统源码解析
java·开发语言·前端·后端·系统架构·uni-app·php
feifeigo1234 天前
matlab画图工具
开发语言·matlab
dustcell.4 天前
haproxy七层代理
java·开发语言·前端
norlan_jame4 天前
C-PHY与D-PHY差异
c语言·开发语言