Kotlinx Serialization 指南
欢迎访问我的主页: https://heeheeaii.github.io/
1. 基础配置
项目配置
// 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")
}
基本使用
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 - 字段名映射
@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"}
}
可选字段和默认值
@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 - 忽略字段
@Serializable
data class User(val id: Int,val name: String,@Transient val password: String = "", // 不会被序列化@Transientval 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. 集合和复杂类型
基本集合
@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)
}
嵌套对象
@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)
@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("加载中...")}}
}
抽象类多态
@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. 自定义序列化器
日期时间序列化
@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_TIMEoverride 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)
}
枚举序列化
@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 序列化器
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 配置选项
常用配置
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)// 解析包含未知字段的 JSONval jsonWithExtra = """{"name": "测试","age": "25","isActive": true,"unknownField": "这个字段会被忽略"}""".trimIndent()val decoded = json.decodeFromString<Config>(jsonWithExtra)println(decoded)// Config(name=测试, age=25, isActive=true)
}
自定义命名策略
// 使用自定义命名策略
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. 错误处理和验证
异常处理
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)}
}
条件序列化
@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 集成
// 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 集成
val client = HttpClient {install(ContentNegotiation) {json(Json {prettyPrint = trueignoreUnknownKeys = 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 响应包装
@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)
}
配置文件处理
@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. 注解使用
@Serializable
data class BestPracticeUser(val id: Int,// 使用 SerialName 映射 API 字段名@SerialName("user_name")val name: String,// 可选字段设置默认值val email: String? = null,// 敏感信息不序列化@Transientval password: String = "",// 自定义序列化器处理复杂类型@Serializable(with = DateTimeSerializer::class)val createdAt: LocalDateTime,// 默认值处理val role: String = "user",val isActive: Boolean = true
)
2. 全局 Json 配置
object JsonConfig {val default = Json {ignoreUnknownKeys = truecoerceInputValues = trueencodeDefaults = falseprettyPrint = BuildConfig.DEBUG // 仅在 Debug 模式下格式化}
}// 在整个应用中使用统一配置
val user = JsonConfig.default.decodeFromString<User>(jsonString)
3. 错误处理
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}") }
)