前言
掌握了序列化器的基本概念和创建方式后,接下来需要学会如何在实际项目中灵活使用它们。本文将系统介绍序列化器的绑定方式、配置策略和动态序列化等实用技巧,帮助您在复杂的业务场景中游刃有余。
一、自动配置(默认行为)
在大多数情况下,我们无需显式指定序列化器,Kotlin Serialization 会根据类型自动选择合适的序列化器。这是最简单也是最常用的方式。
1.1 基本类型自动配置
对于 Kotlin 的基本类型和标准库类型,编译器插件会自动生成或选择对应的序列化器:
kotlin
@Serializable
data class User(
val id: Int, // 自动使用 Int.serializer()
val name: String, // 自动使用 String.serializer()
val isActive: Boolean, // 自动使用 Boolean.serializer()
val score: Double // 自动使用 Double.serializer()
)
// 无需指定任何序列化器,自动配置
val json = Json.encodeToString(User(1, "Alice", true, 95.5))
1.2 集合类型自动配置
集合类型会根据元素类型自动选择序列化器:
kotlin
@Serializable
data class Course(
val title: String,
val tags: List<String>, // 自动使用 ListSerializer(String.serializer())
val scores: Map<String, Int>, // 自动使用 MapSerializer(String.serializer(), Int.serializer())
val optionalInfo: String? // 自动使用 nullable String serializer
)
1.3 嵌套类型自动配置
对于嵌套的自定义类型,只要都标记了 @Serializable
,就会自动配置:
kotlin
@Serializable
data class Address(val city: String, val country: String)
@Serializable
data class Person(
val name: String,
val address: Address // 自动使用 Address 的生成序列化器
)
1.4 自动配置的限制
自动配置有一些限制,以下情况需要显式指定序列化器:
- 第三方库类型(如
java.util.Date
) - 接口和抽象类,后续章节讲解
- 需要特殊序列化格式的类型
kotlin
@Serializable
data class Event(
val name: String,
// val timestamp: Date, // ❌ 编译错误:Date 没有序列化器
// val data: Any, // ❌ 编译错误:Any 没有序列化器
)
二、序列化器绑定
当自动配置无法满足需求时,需要显式绑定序列化器。Kotlin Serialization 提供了多种绑定方式,从临时使用到全局配置,满足不同的使用场景。
2.1 手动传递
最直接的使用方式,适用于临时需要或测试场景:
kotlin
// 定义序列化器
object DateAsLongSerializer : KSerializer<Date> {
// 实现细节...
}
// 手动传递序列化器
val json = Json.encodeToString(DateAsLongSerializer, Date())
val decoded = Json.decodeFromString(DateAsLongSerializer, json)
2.2 属性级别绑定
通过 @Serializable(with = ...)
注解为特定属性指定序列化器:
kotlin
@Serializable
data class Event(
val name: String,
@Serializable(with = DateAsLongSerializer::class)
val timestamp: Date,
)
// 无需手动指定,属性注解自动生效
val json = Json.encodeToString(Event("Meeting", Date()))
2.3 类注解绑定
通过 @Serializable(with = ...)
注解为整个类指定序列化器:
kotlin
// 为整个类指定序列化器
@Serializable(with = DateAsLongSerializer::class)
data class CustomDate(val timestamp: Long)
@Serializable
data class Event(
val name: String,
val timestamp: CustomDate // 使用类注解指定的序列化器
)
2.4 泛型绑定
为泛型参数指定序列化器:
kotlin
@Serializable
data class Schedule(
val events: List<@Serializable(DateAsLongSerializer::class) Date>
)
2.5 文件级别绑定
使用 @file:UseSerializers
为整个文件指定默认序列化器:
kotlin
@file:UseSerializers(DateAsLongSerializer::class)
@Serializable
data class Event(val timestamp: Date) // 自动使用 DateAsLongSerializer
@Serializable
data class Meeting(val startTime: Date, val endTime: Date) // 都使用 DateAsLongSerializer
2.6 别名绑定
通过 typealias
创建序列化类型,可以在别名上再添加注解,添加的注解优先级高于别名本身的配置:
kotlin
// 定义类型别名
typealias DateAsLong = @Serializable(DateAsLongSerializer::class) Date
@Serializable
data class Event(
val name: String,
val timestamp: DateAsLong, // 使用类型别名
// 在别名基础上再添加注解,优先级更高
@Serializable(with = DateAsStringSerializer::class)
val createdAt: DateAsLong // 这里会使用 DateAsStringSerializer 而不是 DateAsLongSerializer
)
三、序列化器动态配置
使用 @Contextual
注解标记需要运行时确定序列化器的字段。运行时在上下文中匹配对应的序列化器,上下文作用域限制在 Json 实例范围内。
3.1 上下文序列化
kotlin
@Serializable
data class Document(
val title: String,
@Contextual // 运行时确定序列化策略
val createdAt: Date,
@Contextual
val metadata: Any, // 可以是任意类型
)
3.2 序列化器模块
通过 SerializersModule
配置运行时序列化策略:
kotlin
// 创建序列化器模块
val dateModule = SerializersModule {
contextual(Date::class, DateAsLongSerializer)
contextual(Any::class) { args ->
// 根据实际类型选择序列化器
when (args[0]) {
String::class -> String.serializer()
Int::class -> Int.serializer()
else -> error("Unsupported type")
}
}
}
// 配置 Json 格式
val json = Json {
serializersModule = dateModule
}
// 使用
val document = Document("Report", Date(), "Important")
val jsonString = json.encodeToString(document)
3.3 多序列化器并存
使用 @KeepGeneratedSerializer
保留插件生成的序列化器:
kotlin
@KeepGeneratedSerializer // 保留插件生成的序列化器
@Serializable(with = ColorAsStringSerializer::class) // 默认使用自定义序列化器
data class Color(val rgb: Int)
// 使用不同的序列化器
val green = Color(0x00ff00)
// 使用自定义序列化器(默认)
val customJson = Json.encodeToString(green) // "00ff00"
// 使用插件生成的序列化器
val generatedJson = Json.encodeToString(Color.generatedSerializer(), green) // {"rgb":65280}
四、序列化器配置优先级
当多种配置方式同时存在时,优先级从高到低为:
手动传递 > 属性注解 > 文件级配置 > 别名绑定 > 上下文配置 > 类注解 > 自动配置
别名绑定额外说明:
- 别名属性上添加额外注解或文件级配置的序列化器优先级更高
- 额外添加的序列化器遵循优先级规则
kotlin
typealias AliasDate = @Serializable(AliasSerializer::class) Date
@Serializable
data class TestEvent(
@Contextual
@Serializable(with = PropertySerializer::class)
val aliasDate: AliasDate,
// PropertySerializer > ContextualSerializer > AliasSerializer
)
下图展示了序列化器使用方式的完整分类和优先级关系:
手动传递
最高优先级"] C --> F["优先级 2
属性注解
@Serializable(with=...)"] C --> I["优先级 3
文件级配置
@file:UseSerializers"] C --> J["优先级 4
别名绑定
typealias"] C --> G["优先级 6
类注解
@Serializable(with=...)"] D["动态绑定"] --> K["优先级 5
上下文配置
@Contextual + Module"] B["自动配置"] --> H["优先级 7
自动配置
默认行为"] classDef root fill:#f5f5f5,stroke:#616161,stroke-width:3px,color:#424242 classDef category fill:#e1f5fe,stroke:#0277bd,stroke-width:2px,color:#01579b classDef priority1 fill:#ffebee,stroke:#f44336,stroke-width:3px,color:#c62828 classDef priority2 fill:#fff3e0,stroke:#ff9800,stroke-width:2px,color:#ef6c00 classDef priority3 fill:#fff8e1,stroke:#ffc107,stroke-width:2px,color:#f57f17 classDef priority4 fill:#f3e5f5,stroke:#9c27b0,stroke-width:2px,color:#7b1fa2 classDef priority5 fill:#e8eaf6,stroke:#3f51b5,stroke-width:2px,color:#303f9f classDef priority6 fill:#e3f2fd,stroke:#2196f3,stroke-width:2px,color:#1565c0 classDef priority7 fill:#e8f5e8,stroke:#4caf50,stroke-width:2px,color:#2e7d32 class A root class B,C,D category class E priority1 class F priority2 class I priority3 class J priority4 class K priority5 class G priority6 class H priority7
五、第三方类序列化
在实际项目中,经常需要序列化第三方库的类(如 java.util.Date
、java.time.LocalDateTime
等)。由于这些类没有 @Serializable
注解,需要特殊处理。
5.1 直接序列化第三方类
当需要直接序列化第三方类时,需要创建序列化器并手动指定,可选方式:
- 手动传递序列化器 :为第三方类创建自定义序列化器,通过
Json.encodeToString(serializer, obj)
直接传递 - 集合序列化器 :使用
ListSerializer
、MapSerializer
等包装第三方类序列化器处理集合
5.2 第三方类作为属性间接引用
当第三方类作为自定义类的属性时,可选处理方式:
- 属性级别绑定 :使用
@Serializable(with = CustomSerializer::class)
为特定属性指定序列化器 - 文件级别配置 :使用
@file:UseSerializers(CustomSerializer::class)
为整个文件统一配置 - 上下文配置 :使用
@Contextual
注解配合SerializersModule
进行运行时配置 - 泛型参数绑定 :为泛型类型参数指定序列化器,如
List<@Serializable(CustomSerializer::class) ThirdPartyClass>
六、最佳实践
6.1 选择合适的绑定方式
- 自动配置:首选方式,适用于基本类型和标准库类型
- 手动传递:临时场景、测试场景、直接序列化第三方类
- 属性注解:特定字段需要特殊处理、第三方类属性
- 类注解:整个类需要统一的序列化策略
- 文件级配置:模块级别统一配置、多个类使用同一第三方类
- 上下文配置:需要运行时动态选择,适用特定业务定制 Json、第三方类动态配置
6.2 性能考虑
手写序列化器推荐使用 object
,避免序列化过程中重复创建:
kotlin
// 推荐:使用 object 单例
object DateSerializer : KSerializer<Date> { /* ... */ }
// 不推荐:每次创建新实例
class DateSerializer : KSerializer<Date> { /* ... */ }
6.3 错误处理策略
序列化器的错误处理应该根据业务需求选择合适的策略:
策略1:不处理异常(推荐)
默认情况下应该选择不处理异常,让异常向上传播,由业务层决定如何处理。
策略2:默认值兜底异常
仅在特定场景下使用,需要注意这会让业务逻辑污染数据层的逻辑:
kotlin
object SafeDateSerializer : KSerializer<Date> {
override val descriptor = PrimitiveSerialDescriptor("SafeDate", PrimitiveKind.LONG)
override fun serialize(encoder: Encoder, value: Date) {
encoder.encodeLong(value.time)
}
override fun deserialize(decoder: Decoder): Date {
return try {
Date(decoder.decodeLong())
} catch (e: Exception) {
Date(0) // 默认值兜底,但这是业务逻辑的决策
}
}
}
选择建议:
- 策略1:适用于大多数场景,保持数据层的纯净性,异常处理交给业务层
- 策略2:仅在确实需要容错且有明确默认值语义的场景下使用
结语
掌握这些使用技巧,能够让您在各种复杂的业务场景中都能游刃有余地使用 Kotlin Serialization,构建出既高效又优雅的序列化解决方案。