当前位置: 首页 > news >正文

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}") }
)
http://www.dtcms.com/a/402941.html

相关文章:

  • SpringBoot @Scheduled 注解详解
  • layui 表格行级 upload 上传操作
  • 【Unity 入门教程】三、如何设置自定义字体(解决中文乱码问题)
  • STM32开发(FreeRTOS实时操作系统)
  • RocketMQ-生产常见问题汇总
  • 成都网站托管外包施工企业科技宣传片
  • 小厂 Java 面试,难度怎么样?
  • Webpack5 第一节
  • 【深入理解JVM】常见的垃圾回收器
  • 东莞企业建设网站官网有限公司百度一下百度网页版主页
  • 【大模型:知识图谱】--7.Neo4j数据库的导入和导出
  • 数据结构与算法(栈)
  • Coze源码分析-资源库-创建数据库-后端源码-基础设施/数据存储层
  • PySpark 安装教程及 WordCount 实战与任务提交
  • 制作网站的公司八大建筑央企排名
  • zynq纯PL读取XADC
  • 【FastMCP】中间件
  • bigo二面总结
  • 个人网站建设思路省级别网站建设方案
  • 测试自动化教程:Parasoft如何流重定向与单元测试自动化
  • 开源AI大模型、AI智能名片与S2B2C商城小程序在价值观型社群构建与运营中的价值与应用
  • 郑州 网站建设公司阿里企业邮箱收费标准一年多少钱
  • Day03:小程序的常用操作
  • 交互的脉络:小程序事件系统详解
  • 自助建站免费平台深圳建设管理中心网站首页
  • LVS虚拟调度器学习
  • 【LVS入门宝典】LVS-TUN模式原理与配置:跨越网络界限的负载均衡解决方案
  • 【LVS入门宝典】LVS-TUN模式配置实战以及配置关键点:Real Server的路由表调整、ipip模块加载
  • LVS、Nginx、HAProxy 的区别
  • 是什么让边缘电脑真正工业化?