引言
在 Kotlin 开发中,JSON 序列化是每个开发者都会遇到的基础需求。面对众多的 JSON 处理库,如何选择最适合项目的方案?本文将通过详细对比 Kotlinx.serialization 和 Gson,帮助你做出明智的技术选型。
快速概览
| 特性 | 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 默认值
Kotlindata 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步:渐进迁移 - 开始逐步迁移数据类和业务逻辑
结束。