Kotlinx Serialization 指南

欢迎访问我的主页: https://heeheeaii.github.io/

1. 基础配置

项目配置

kotlin 复制代码
// build.gradle.kts (Module)
plugins {
    kotlin("jvm") version "1.9.10"
    kotlin("plugin.serialization") version "1.9.10"
}

dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0")
}

基本使用

kotlin 复制代码
import kotlinx.serialization.*
import kotlinx.serialization.json.*

@Serializable
data class User(
    val id: Int,
    val name: String,
    val email: String
)

fun main() {
    val user = User(1, "张三", "zhangsan@example.com")
    
    // 序列化:对象 → JSON 字符串
    val jsonString = Json.encodeToString(user)
    println(jsonString)
    // {"id":1,"name":"张三","email":"zhangsan@example.com"}
    
    // 反序列化:JSON 字符串 → 对象
    val decodedUser = Json.decodeFromString<User>(jsonString)
    println(decodedUser)
    // User(id=1, name=张三, email=zhangsan@example.com)
}

2. 字段映射和配置

@SerialName - 字段名映射

kotlin 复制代码
@Serializable
data class User(
    val id: Int,
    @SerialName("user_name") 
    val name: String,
    @SerialName("email_address")
    val email: String,
    @SerialName("created_at")
    val createdAt: String
)

fun testSerialName() {
    val user = User(1, "张三", "zhang@example.com", "2023-01-01")
    val json = Json.encodeToString(user)
    println(json)
    // {"id":1,"user_name":"张三","email_address":"zhang@example.com","created_at":"2023-01-01"}
}

可选字段和默认值

kotlin 复制代码
@Serializable
data class Product(
    val id: Int,
    val name: String,
    val price: Double,
    val description: String? = null,  // 可为空
    val category: String = "未分类",   // 默认值
    val isActive: Boolean = true
)

fun testDefaultValues() {
    // JSON 中缺少某些字段
    val json = """{"id":1,"name":"iPhone","price":999.99}"""
    
    val product = Json.decodeFromString<Product>(json)
    println(product)
    // Product(id=1, name=iPhone, price=999.99, description=null, category=未分类, isActive=true)
}

@Transient - 忽略字段

kotlin 复制代码
@Serializable
data class User(
    val id: Int,
    val name: String,
    @Transient 
    val password: String = "",  // 不会被序列化
    @Transient
    val tempData: Map<String, Any> = emptyMap()
)

fun testTransient() {
    val user = User(1, "张三", "secret123", mapOf("cache" to "data"))
    val json = Json.encodeToString(user)
    println(json)
    // {"id":1,"name":"张三"}  // password 和 tempData 被忽略
}

3. 集合和复杂类型

基本集合

kotlin 复制代码
@Serializable
data class UserList(
    val users: List<User>,
    val tags: Set<String>,
    val metadata: Map<String, String>
)

fun testCollections() {
    val userList = UserList(
        users = listOf(
            User(1, "张三", "zhang@example.com"),
            User(2, "李四", "li@example.com")
        ),
        tags = setOf("admin", "user"),
        metadata = mapOf("total" to "2", "page" to "1")
    )
    
    val json = Json.encodeToString(userList)
    println(Json { prettyPrint = true }.encodeToString(userList))
}

// 直接序列化集合
fun testDirectCollections() {
    val users = listOf(
        User(1, "张三", "zhang@example.com"),
        User(2, "李四", "li@example.com")
    )
    
    // List<User> 直接序列化
    val json = Json.encodeToString(users)
    println(json)
    
    // 反序列化为 List<User>
    val decodedUsers = Json.decodeFromString<List<User>>(json)
    println(decodedUsers)
}

嵌套对象

kotlin 复制代码
@Serializable
data class Address(
    val street: String,
    val city: String,
    val zipCode: String
)

@Serializable
data class Company(
    val id: Int,
    val name: String,
    val address: Address,  // 嵌套对象
    val employees: List<User>  // 嵌套列表
)

fun testNestedObjects() {
    val company = Company(
        id = 1,
        name = "科技有限公司",
        address = Address("中关村大街1号", "北京", "100000"),
        employees = listOf(
            User(1, "张三", "zhang@example.com"),
            User(2, "李四", "li@example.com")
        )
    )
    
    val json = Json { prettyPrint = true }.encodeToString(company)
    println(json)
}

4. 多态序列化

密封类 (Sealed Class)

kotlin 复制代码
@Serializable
sealed class ApiResponse {
    @Serializable
    @SerialName("success")
    data class Success(val data: String, val timestamp: Long) : ApiResponse()
    
    @Serializable
    @SerialName("error")
    data class Error(val message: String, val code: Int) : ApiResponse()
    
    @Serializable
    @SerialName("loading")
    object Loading : ApiResponse()
}

fun testPolymorphism() {
    val responses: List<ApiResponse> = listOf(
        ApiResponse.Success("数据获取成功", System.currentTimeMillis()),
        ApiResponse.Error("网络错误", 500),
        ApiResponse.Loading
    )
    
    // 序列化多态对象
    val json = Json { prettyPrint = true }.encodeToString(responses)
    println(json)
    
    // 反序列化
    val decodedResponses = Json.decodeFromString<List<ApiResponse>>(json)
    decodedResponses.forEach { response ->
        when (response) {
            is ApiResponse.Success -> println("成功: ${response.data}")
            is ApiResponse.Error -> println("错误: ${response.message}")
            is ApiResponse.Loading -> println("加载中...")
        }
    }
}

抽象类多态

kotlin 复制代码
@Serializable
@JsonClassDiscriminator("type")
abstract class Shape

@Serializable
@SerialName("circle")
data class Circle(val radius: Double) : Shape()

@Serializable
@SerialName("rectangle")
data class Rectangle(val width: Double, val height: Double) : Shape()

@Serializable
@SerialName("triangle")
data class Triangle(val base: Double, val height: Double) : Shape()

fun testAbstractPolymorphism() {
    val shapes: List<Shape> = listOf(
        Circle(5.0),
        Rectangle(10.0, 8.0),
        Triangle(6.0, 4.0)
    )
    
    val json = Json { prettyPrint = true }.encodeToString(shapes)
    println(json)
    
    val decodedShapes = Json.decodeFromString<List<Shape>>(json)
    println(decodedShapes)
}

5. 自定义序列化器

日期时间序列化

kotlin 复制代码
@Serializable
data class Event(
    val id: Int,
    val name: String,
    @Serializable(with = DateTimeSerializer::class)
    val createdAt: LocalDateTime
)

object DateTimeSerializer : KSerializer<LocalDateTime> {
    private val formatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME
    
    override val descriptor: SerialDescriptor = 
        PrimitiveSerialDescriptor("LocalDateTime", PrimitiveKind.STRING)
    
    override fun serialize(encoder: Encoder, value: LocalDateTime) {
        encoder.encodeString(value.format(formatter))
    }
    
    override fun deserialize(decoder: Decoder): LocalDateTime {
        return LocalDateTime.parse(decoder.decodeString(), formatter)
    }
}

fun testCustomSerializer() {
    val event = Event(1, "会议", LocalDateTime.now())
    val json = Json.encodeToString(event)
    println(json)
    
    val decodedEvent = Json.decodeFromString<Event>(json)
    println(decodedEvent)
}

枚举序列化

kotlin 复制代码
@Serializable
enum class Status(val value: String) {
    @SerialName("active") ACTIVE("激活"),
    @SerialName("inactive") INACTIVE("未激活"),
    @SerialName("pending") PENDING("待审核");
    
    @Serializer(forClass = Status::class)
    companion object : KSerializer<Status> {
        override val descriptor: SerialDescriptor = 
            PrimitiveSerialDescriptor("Status", PrimitiveKind.STRING)
        
        override fun serialize(encoder: Encoder, value: Status) {
            encoder.encodeString(value.name.lowercase())
        }
        
        override fun deserialize(decoder: Decoder): Status {
            val name = decoder.decodeString().uppercase()
            return Status.valueOf(name)
        }
    }
}

@Serializable
data class User(
    val id: Int,
    val name: String,
    val status: Status
)

BigDecimal 序列化器

kotlin 复制代码
object BigDecimalSerializer : KSerializer<BigDecimal> {
    override val descriptor: SerialDescriptor = 
        PrimitiveSerialDescriptor("BigDecimal", PrimitiveKind.STRING)
    
    override fun serialize(encoder: Encoder, value: BigDecimal) {
        encoder.encodeString(value.toPlainString())
    }
    
    override fun deserialize(decoder: Decoder): BigDecimal {
        return BigDecimal(decoder.decodeString())
    }
}

@Serializable
data class Product(
    val id: Int,
    val name: String,
    @Serializable(with = BigDecimalSerializer::class)
    val price: BigDecimal
)

6. Json 配置选项

常用配置

kotlin 复制代码
val json = Json {
    // 忽略 JSON 中未知的字段
    ignoreUnknownKeys = true
    
    // 美化输出
    prettyPrint = true
    
    // 类型强制转换 (如字符串 "1" 转为数字 1)
    coerceInputValues = true
    
    // 编码默认值
    encodeDefaults = true
    
    // 允许结构化键 (Map 的键可以是复杂对象)
    allowStructuredMapKeys = true
    
    // 允许特殊浮点值 (NaN, Infinity)
    allowSpecialFloatingPointValues = true
}

@Serializable
data class Config(
    val name: String,
    val age: Int = 18,
    val isActive: Boolean = true
)

fun testJsonConfig() {
    val config = Config("测试")
    
    // 使用配置的 Json 实例
    val jsonString = json.encodeToString(config)
    println(jsonString)
    
    // 解析包含未知字段的 JSON
    val jsonWithExtra = """
        {
            "name": "测试",
            "age": "25",
            "isActive": true,
            "unknownField": "这个字段会被忽略"
        }
    """.trimIndent()
    
    val decoded = json.decodeFromString<Config>(jsonWithExtra)
    println(decoded)
    // Config(name=测试, age=25, isActive=true)
}

自定义命名策略

kotlin 复制代码
// 使用自定义命名策略
val snakeCaseJson = Json {
    namingStrategy = JsonNamingStrategy.SnakeCase
}

@Serializable
data class UserProfile(
    val userId: Int,
    val firstName: String,
    val lastName: String,
    val emailAddress: String
)

fun testNamingStrategy() {
    val profile = UserProfile(1, "张", "三", "zhangsan@example.com")
    
    val json = snakeCaseJson.encodeToString(profile)
    println(json)
    // {"user_id":1,"first_name":"张","last_name":"三","email_address":"zhangsan@example.com"}
}

7. 错误处理和验证

异常处理

kotlin 复制代码
fun safeDeserialization() {
    val invalidJson = """{"id": "not_a_number", "name": "张三"}"""
    
    try {
        val user = Json.decodeFromString<User>(invalidJson)
        println(user)
    } catch (e: SerializationException) {
        println("序列化错误: ${e.message}")
        // 可以提供默认值或其他处理逻辑
    }
}

// 更优雅的错误处理
fun safeParseUser(jsonString: String): Result<User> {
    return try {
        Result.success(Json.decodeFromString<User>(jsonString))
    } catch (e: SerializationException) {
        Result.failure(e)
    }
}

条件序列化

kotlin 复制代码
@Serializable
data class ConditionalUser(
    val id: Int,
    val name: String,
    @EncodeDefault(EncodeDefault.Mode.NEVER)  // 从不编码默认值
    val role: String = "user",
    @EncodeDefault(EncodeDefault.Mode.ALWAYS) // 总是编码默认值
    val isActive: Boolean = true
)

8. 与网络库集成

与 OkHttp + Retrofit 集成

kotlin 复制代码
// Retrofit 配置
val retrofit = Retrofit.Builder()
    .baseUrl("https://api.example.com/")
    .addConverterFactory(
        Json.asConverterFactory("application/json".toMediaType())
    )
    .client(okHttpClient)
    .build()

// API 接口
interface ApiService {
    @GET("users/{id}")
    suspend fun getUser(@Path("id") id: Int): User
    
    @POST("users")
    suspend fun createUser(@Body user: User): User
    
    @GET("users")
    suspend fun getUsers(): List<User>
}

与 Ktor Client 集成

kotlin 复制代码
val client = HttpClient {
    install(ContentNegotiation) {
        json(Json {
            prettyPrint = true
            ignoreUnknownKeys = true
        })
    }
}

// 使用
suspend fun fetchUser(id: Int): User {
    return client.get("https://api.example.com/users/$id").body()
}

suspend fun createUser(user: User): User {
    return client.post("https://api.example.com/users") {
        contentType(ContentType.Application.Json)
        setBody(user)
    }.body()
}

9. 实际应用示例

API 响应包装

kotlin 复制代码
@Serializable
data class ApiResponse<T>(
    val code: Int,
    val message: String,
    val data: T? = null,
    val timestamp: Long = System.currentTimeMillis()
)

@Serializable
data class PaginatedResponse<T>(
    val items: List<T>,
    val total: Int,
    val page: Int,
    val pageSize: Int,
    val hasNext: Boolean
)

// 使用示例
suspend fun fetchUsers(page: Int = 1): ApiResponse<PaginatedResponse<User>> {
    val json = """
        {
            "code": 200,
            "message": "success",
            "data": {
                "items": [
                    {"id": 1, "name": "张三", "email": "zhang@example.com"},
                    {"id": 2, "name": "李四", "email": "li@example.com"}
                ],
                "total": 100,
                "page": 1,
                "pageSize": 10,
                "hasNext": true
            },
            "timestamp": 1635724800000
        }
    """.trimIndent()
    
    return Json.decodeFromString<ApiResponse<PaginatedResponse<User>>>(json)
}

配置文件处理

kotlin 复制代码
@Serializable
data class AppConfig(
    val server: ServerConfig,
    val database: DatabaseConfig,
    val logging: LoggingConfig
)

@Serializable
data class ServerConfig(
    val host: String = "localhost",
    val port: Int = 8080,
    val ssl: Boolean = false
)

@Serializable
data class DatabaseConfig(
    val url: String,
    val username: String,
    val password: String,
    val maxConnections: Int = 10
)

@Serializable
data class LoggingConfig(
    val level: String = "INFO",
    val file: String? = null
)

// 从文件读取配置
fun loadConfig(configFile: File): AppConfig {
    val json = configFile.readText()
    return Json.decodeFromString<AppConfig>(json)
}

// 保存配置到文件
fun saveConfig(config: AppConfig, configFile: File) {
    val json = Json { prettyPrint = true }.encodeToString(config)
    configFile.writeText(json)
}

10. 最佳实践总结

1. 注解使用

kotlin 复制代码
@Serializable
data class BestPracticeUser(
    val id: Int,
    
    // 使用 SerialName 映射 API 字段名
    @SerialName("user_name")
    val name: String,
    
    // 可选字段设置默认值
    val email: String? = null,
    
    // 敏感信息不序列化
    @Transient
    val password: String = "",
    
    // 自定义序列化器处理复杂类型
    @Serializable(with = DateTimeSerializer::class)
    val createdAt: LocalDateTime,
    
    // 默认值处理
    val role: String = "user",
    val isActive: Boolean = true
)

2. 全局 Json 配置

kotlin 复制代码
object JsonConfig {
    val default = Json {
        ignoreUnknownKeys = true
        coerceInputValues = true
        encodeDefaults = false
        prettyPrint = BuildConfig.DEBUG  // 仅在 Debug 模式下格式化
    }
}

// 在整个应用中使用统一配置
val user = JsonConfig.default.decodeFromString<User>(jsonString)

3. 错误处理

kotlin 复制代码
inline fun <reified T> String.parseJson(): Result<T> {
    return try {
        Result.success(Json.decodeFromString<T>(this))
    } catch (e: SerializationException) {
        Result.failure(e)
    }
}

// 使用
val result = jsonString.parseJson<User>()
result.fold(
    onSuccess = { user -> println("解析成功: $user") },
    onFailure = { error -> println("解析失败: ${error.message}") }
)
相关推荐
用户099 小时前
MVI架构如何改变Android开发模式
android·面试·kotlin
小孔龙9 小时前
K2 编译器 - Symbol(符号系统)
kotlin·编译器
phoneixsky1 天前
Kotlin的各种上下文Receiver,到底怎么个事
kotlin
heeheeai1 天前
okhttp使用指南
okhttp·kotlin·教程
Monkey-旭1 天前
Android 注解完全指南:从基础概念到自定义实战
android·java·kotlin·注解·annotation
alexhilton2 天前
如何构建Android应用:深入探讨原则而非规则
android·kotlin·android jetpack
TeleostNaCl2 天前
SMBJ 简单使用指南 实现在 Java/Android 程序中访问 SMB 服务器
android·java·运维·服务器·经验分享·kotlin
小孔龙2 天前
Kotlin 序列化:重复引用是技术问题还是架构缺陷?
android·kotlin·json
Kapaseker2 天前
每个Kotlin开发者应该掌握的最佳实践,第三趴
android·kotlin