概述
Kotlin Serialization 的 JSON 工具提供了丰富的配置选项,允许开发者根据不同需求定制 JSON 序列化和反序列化行为。默认的 Json 实现严格遵循标准 JSON 规范,但通过创建自定义 Json 实例,可以支持许多非标准 JSON 特性。
注意:部分 API 处于实验阶段(
ExperimentalSerializationApi),使用时需要添加相应注解。
创建自定义 JSON 实例
要使用自定义 JSON 格式配置,需要通过 Json() 构建器函数创建自定义 Json 实例:
val format = Json {
prettyPrint = true
ignoreUnknownKeys = true
// 其他配置选项...
}
性能提示:建议存储和重用自定义
Json实例,因为Json会缓存序列化器相关信息以提升性能。
JSON 配置选项
影响范围说明:配置选项的影响范围分为三类:
- 序列化:仅影响对象转 JSON 的过程
- 反序列化:仅影响 JSON 转对象的过程
- 双向:同时影响序列化和反序列化过程
每个配置选项都会明确标注其影响范围。
1. 美化打印 (Pretty Printing)
配置属性:prettyPrint
默认值:false
影响范围:序列化
功能:控制 JSON 输出是否包含缩进和换行,提高可读性。
val format = Json { prettyPrint = true }
@Serializable
data class Animal(val name: String, val species: String)
fun main() {
println(format.encodeToString(Animal("Fluffy", "Cat")))
}
输出结果:
{
"name": "Fluffy",
"species": "Cat"
}
2. 宽松解析 (Lenient Parsing)
配置属性:isLenient
默认值:false
影响范围:反序列化
功能:放宽 JSON 解析限制,允许解析非标准格式的 JSON 数据。
val format = Json { isLenient = true }
@Serializable
data class Animal(val name: String, val age: Int)
fun main() {
val data = format.decodeFromString<Animal>("""
{
name : Fluffy,
age : "3"
}
""")
println(data)
}
宽松解析支持以下非标准格式:
- 不带引号的属性名
- 不带引号的字符串值
- 不带引号的枚举值
- 字符串形式的数字值
3. 忽略未知属性 (Ignoring Unknown Keys)
配置属性:ignoreUnknownKeys
默认值:false
影响范围:反序列化
功能:在反序列化时忽略 JSON 中存在但 Kotlin 类中不存在的属性。默认情况下,未知属性会导致反序列化失败。
val format = Json { ignoreUnknownKeys = true }
@Serializable
data class Animal(val name: String)
fun main() {
val data = format.decodeFromString<Animal>("""
{"name":"Fluffy","species":"Cat"}
""")
println(data)
// 输出:Animal(name=Fluffy)
}
按类忽略未知属性
使用 @JsonIgnoreUnknownKeys 注解可以仅对特定类启用此功能:
@Serializable
@JsonIgnoreUnknownKeys
data class Animal(val name: String)
fun main() {
// 此类会自动忽略未知属性,不会抛出异常
val animal = Json.decodeFromString<Animal>("""{"name":"Fluffy","species":"Cat","age":3}""")
println(animal) // Animal(name=Fluffy)
}
按属性处理特殊情况
对于特定属性,可以使用 @Transient 注解跳过序列化和反序列化:
@Serializable
data class Animal(
val name: String,
val species: String,
@Transient val tempData: String = "default" // 不参与序列化/反序列化
)
fun main() {
val json = """{"name":"Fluffy","species":"Cat","tempData":"ignored"}"""
val animal = Json.decodeFromString<Animal>(json)
println(animal) // Animal(name=Fluffy, species=Cat, tempData=default)
val encoded = Json.encodeToString(animal)
println(encoded) // {"name":"Fluffy","species":"Cat"}
}
也可以使用 @SerialName 配合条件逻辑来处理属性级别的特殊情况:
@Serializable
data class FlexibleAnimal(
val name: String,
@SerialName("type") val species: String? = null,
val details: Map<String, String> = emptyMap()
)
fun main() {
// 可以处理不同结构的 JSON 数据
val json1 = """{"name":"Fluffy","type":"Cat"}"""
val json2 = """{"name":"Buddy","species":"Dog","details":{"color":"brown"}}"""
val animal1 = Json.decodeFromString<FlexibleAnimal>(json1)
val animal2 = Json { ignoreUnknownKeys = true }.decodeFromString<FlexibleAnimal>(json2)
println(animal1) // FlexibleAnimal(name=Fluffy, species=Cat, details={})
println(animal2) // FlexibleAnimal(name=Buddy, species=null, details={})
}
4. 备选 JSON 属性名 (Alternative Json Names)
注解:@JsonNames
影响范围:反序列化
功能:为单个 Kotlin 属性支持多个 JSON 字段名称,提高 API 兼容性。
@OptIn(ExperimentalSerializationApi::class)
@Serializable
data class Animal(@JsonNames("petName") val name: String)
fun main() {
// 两种 JSON 字段名都可以正确解析
val animal1 = Json.decodeFromString<Animal>("""{"name":"Fluffy"}""")
val animal2 = Json.decodeFromString<Animal>("""{"petName":"Buddy"}""")
println(animal1) // Animal(name=Fluffy)
println(animal2) // Animal(name=Buddy)
}
5. 编码默认值 (Encoding Defaults)
配置属性:encodeDefaults
默认值:false
影响范围:序列化
功能:控制是否在 JSON 中输出属性的默认值。
val format = Json { encodeDefaults = true }
@Serializable
class Animal(
val name: String,
val species: String = "Cat",
val owner: String? = null
)
fun main() {
val data = Animal("Fluffy")
println(format.encodeToString(data))
// 输出:{"name":"Fluffy","species":"Cat","owner":null}
}
按属性控制默认值编码
使用 @EncodeDefault 注解可以精确控制特定属性的默认值编码行为,无需全局设置:
@Serializable
data class Animal(
val name: String,
@EncodeDefault val species: String = "Cat", // 总是编码
@EncodeDefault(EncodeDefault.Mode.NEVER) val age: Int = 0, // 从不编码
val owner: String? = null // 跟随全局设置
)
fun main() {
val animal = Animal("Fluffy")
// 不设置 encodeDefaults,默认为 false
println(Json.encodeToString(animal))
// 输出:{"name":"Fluffy","species":"Cat"}
// species 被编码(因为 @EncodeDefault),age 不被编码(因为 NEVER)
// 设置 encodeDefaults = true
val format = Json { encodeDefaults = true }
println(format.encodeToString(animal))
// 输出:{"name":"Fluffy","species":"Cat","owner":null}
// age 仍然不被编码(因为 @EncodeDefault(NEVER) 优先级更高)
}
@EncodeDefault** 模式说明**:
@EncodeDefault(默认):总是编码此属性的默认值@EncodeDefault(EncodeDefault.Mode.ALWAYS):同上,显式指定@EncodeDefault(EncodeDefault.Mode.NEVER):从不编码此属性的默认值
6. 显式空值 (Explicit Nulls)
配置属性:explicitNulls
默认值:true
影响范围:双向
功能:控制可空属性的 null 值处理方式。
当设置为 false 时:
- 序列化:null 值属性不会输出到 JSON 中
- 反序列化:缺失的字段被视为 null 或默认值
val format = Json { explicitNulls = false }
@Serializable
data class Animal(
val name: String,
val species: String,
val age: Int? = 3,
val owner: String?,
val description: String? = null
)
fun main() {
val data = Animal("Fluffy", "Cat", null, null, null)
val json = format.encodeToString(data)
println(json)
// 输出:{"name":"Fluffy","species":"Cat"}
println(format.decodeFromString<Animal>(json))
// 输出:Animal(name=Fluffy, species=Cat, age=3, owner=null, description=null)
}
示例说明:
age属性在encode前是null,encode不会序列化age字段,decode后变成了3(默认值)owner属性没有默认值,所以decode后仍然是nulldescription属性有默认值null,decode后保持为null
注意:当
explicitNulls = false时,具有非空默认值的可空属性的编码/解码会变得不对称。这种行为在处理可选字段时很有用,但需要注意数据一致性。
7. 强制输入值 (Coercing Input Values)
配置属性:coerceInputValues
默认值:false
影响范围:反序列化
功能:将无效输入值转换为默认值或 null,提高容错性。
支持处理的无效值:
- 非空类型接收到
null值 - 枚举类型接收到未知值
val format = Json { coerceInputValues = true }
@Serializable
data class Animal(val name: String, val species: String = "Cat")
fun main() {
val data = format.decodeFromString<Animal>("""
{"name":"Fluffy","species":null}
""")
println(data) // Animal(name=Fluffy, species=Cat)
}
8. 允许复杂类型作为 Map 键 (Allowing Structured Map Keys)
配置属性:allowStructuredMapKeys
默认值:false
影响范围:双向
功能:允许使用自定义类作为 Map 的键类型。
val format = Json { allowStructuredMapKeys = true }
@Serializable
data class Animal(val name: String)
fun main() {
val map = mapOf(
Animal("Fluffy") to "Cat",
Animal("Buddy") to "Dog"
)
println(format.encodeToString(map))
// 输出:[{"name":"Fluffy"},"Cat",{"name":"Buddy"},"Dog"]
// 注意:Map 被序列化为交替的键值数组
}
9. 允许特殊浮点值 (Allowing Special Floating-Point Values)
配置属性:allowSpecialFloatingPointValues
默认值:false
影响范围:双向
功能:支持序列化 NaN、Infinity 等特殊浮点值。
val format = Json { allowSpecialFloatingPointValues = true }
@Serializable
class Animal(val weight: Double)
fun main() {
val data = Animal(Double.NaN)
println(format.encodeToString(data))
// 输出:{"weight":NaN}
// 注意:这不是标准 JSON,但在某些场景下很有用
}
10. 类判别器输出模式 (Class Discriminator Output Mode)
配置属性:classDiscriminatorMode
默认值:ClassDiscriminatorMode.POLYMORPHIC
影响范围:序列化
功能:控制多态类型判别器的输出策略。
可选值:
POLYMORPHIC(默认):仅为多态类添加判别器NONE:不添加判别器ALL_JSON_OBJECTS:为所有可能的对象添加判别器
11. 枚举反序列化不区分大小写 (Case-Insensitive Enum Decoding)
配置属性:decodeEnumsCaseInsensitive
默认值:false
影响范围:反序列化
功能:在反序列化时忽略枚举值的大小写。
@OptIn(ExperimentalSerializationApi::class)
val format = Json { decodeEnumsCaseInsensitive = true }
enum class AnimalType { DOMESTIC, @JsonNames("Wild") WILD }
@Serializable
data class AnimalList(val animals: List<AnimalType>)
fun main() {
println(format.decodeFromString<AnimalList>("""{"animals":["domestic", "wild"]}"""))
// 输出:AnimalList(animals=[DOMESTIC, WILD])
}
12. 全局命名策略 (Global Naming Strategy)
配置属性:namingStrategy
默认值:无(使用原始属性名)
影响范围:双向
功能:为所有属性名应用统一的命名转换策略。
可选策略:
JsonNamingStrategy.SnakeCase:下划线命名(如:animal_name)JsonNamingStrategy.KebabCase:短横线命名(如:animal-name)
@Serializable
data class Animal(val animalName: String, val animalOwner: String)
@OptIn(ExperimentalSerializationApi::class)
val snakeCaseFormat = Json { namingStrategy = JsonNamingStrategy.SnakeCase }
@OptIn(ExperimentalSerializationApi::class)
val kebabCaseFormat = Json { namingStrategy = JsonNamingStrategy.KebabCase }
fun main() {
val animal = Animal("Fluffy", "Alice")
// SnakeCase 示例
println("SnakeCase:")
println(snakeCaseFormat.encodeToString(animal))
// 输出:{"animal_name":"Fluffy","animal_owner":"Alice"}
val decodedSnake = snakeCaseFormat.decodeFromString<Animal>("""{"animal_name":"Buddy", "animal_owner":"Bob"}""")
println(decodedSnake)
// 输出:Animal(animalName=Buddy, animalOwner=Bob)
// KebabCase 示例
println("\nKebabCase:")
println(kebabCaseFormat.encodeToString(animal))
// 输出:{"animal-name":"Fluffy","animal-owner":"Alice"}
val decodedKebab = kebabCaseFormat.decodeFromString<Animal>("""{"animal-name":"Charlie", "animal-owner":"Carol"}""")
println(decodedKebab)
// 输出:Animal(animalName=Charlie, animalOwner=Carol)
}
使用注意:
- 命名策略应用于所有属性,包括使用
@SerialName指定的属性 - 可能在复杂类层次中导致名称冲突
- 影响 IDE 的重构和查找功能
- 建议在项目初期统一规划使用
基础配置总结
本文档涵盖了 Kotlin Serialization 的基础 JSON 配置选项,包括:
- 美化打印 - 控制 JSON 输出格式
- 宽松解析 - 支持非标准 JSON 格式
- 忽略未知属性 - 处理 API 演进和兼容性
- 备选 JSON 属性名 - 支持多个字段名称
- 编码默认值 - 控制默认值的输出
- 显式空值 - 管理 null 值的处理
- 强制输入值 - 处理无效输入数据
- 允许非基础类型作为Map的key - 支持复杂键类型
- 允许特殊浮点值 - 处理 NaN 和无穷大
- 类判别器输出模式 - 控制多态类型信息
- 枚举解码不区分大小写 - 灵活的枚举处理
- 全局命名策略 - 统一的属性名称转换
这些基础配置选项为大多数常见的 JSON 序列化需求提供了解决方案。每个选项都明确标注了影响范围,帮助开发者理解其适用范围。