Serialization由于其使用Kotlin的KType而非Java, 这里推荐使用
Net
网络请求库中的转换器. 可以实现指定任何泛型解析结果:
接入文档
.
使用Net可以编写最优雅的请求代码
项目 build.gradle
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
// 和Kotlin插件同一个版本号即可
module build.gradle
apply plugin: 'kotlinx-serialization'
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.0"
JSON使用
函数 | 描述 |
---|
encodeToString | 序列化到字符串 |
encodeToJsonElement | 序列化到JsonElement |
decodeFromString | 反序列化自字符串 |
decodeFromJsonElement | 反序列化自JsonElement |
val json:String = Json.encodeToString(Model("彭于晏", 23))
val json = """{"name":"彭于晏","age":33}"""
val model = Json.decodeFromString<Model>(json)
仅属性支持序列化(即包含setter和getter)
默认值不会被序列化, 即使是可空属性
@Serializable
class Project(val name: String, val language: String)
fun main() {
val data = Project("kotlinx.serialization", "Kotlin")
println(Json.encodeToString(data))
@Serializable
class Project(val name: String, val language: String)
fun main() {
val data = Project("kotlinx.serialization", "Kotlin")
println(Json.encodeToString(data))
私有构造函数, 暴露其他构造函数, 这样Serialization会序列化暴露出来的构造函数属性
当数据类字段比JSON多出属性会序列化失败, 除非多出的属性存在默认值(@Required
则强制要求JSON匹配字段)
序列化时如果存在循环结构字段, 会导致堆栈溢出, 建议你忽略排除该字段
当JSON字段覆盖属性值时, 属性值的默认值为一个函数, 该函数不会被调用
JSON覆盖存在默认值的属性会错误
注解 | 修饰位置 | 描述 |
---|
@Transient | 字段 | 忽略字段 |
@Required | 字段 | 强制有默认值的参数也要匹配JSON字段 |
@SerialName | 字段 | 修饰类为指定序列者的名称, 修饰字段为指定在JSON中的名称 |
@JsonNames | 字段 | 可以为字段再指定多个名称, 同时字段名也不会失效 |
@SerialInfo | 类 | 允许编译器将注释信息保存到SerialDescriptor中, 使用getElementAnnotations |
@Serializer | 类 | 其指定参数为目标类, 目标类以其修饰类创建序列化器 |
暂时不支持无符号类型/内联类
单例对象不支持被序列化(Unit属于单例), 序列化结果为{}
父类的属性不会被序列化
对象只有赋值才会被序列化(赋值null也会参与), 存在默认值也不会(除非使用@Required修饰).
委托字段/Val字段都不会参与序列化. 只有同时拥有set/get才会被序列化(构造参数允许val)
私有构造函数的字段也会被序列化, 如果构造参数非val或者var也不会参与序列化
@Serializable
class Model(var name: String, var age: Int) {
var height: Long? = 23
或者直接在文件中声明序列者
@file:UseSerializers(DateAsLongSerializer::class)
自定义序列者, BoxSerializer
既自定义的序列化器
@Serializable(with = BoxSerializer::class)
data class Box<T>(val contents: T)
当类无法被修饰时我们可以为序列器使用修饰符@Serializer
@Serializer(forClass = Project::class)
object ProjectSerializer
不仅仅是构造参数才会被序列, 只有拥有getter/setter函数的属性都会参与
父类被@Serializable修饰, 其子类也必须被修饰
@Serializable
open class Project(val name: String)
class OwnedProject(name: String, val owner: String) : Project(name)
fun main() {
val data: Project = OwnedProject("kotlinx.coroutines", "kotlin")
println(Json.encodeToString(data))
val data = OwnedProject("kotlinx.coroutines", "kotlin")
仅密封类允许抽象属性, 如果不是密封类则不允许
@Serializable
sealed class Project {
abstract val name: String
@Serializable
class OwnedProject(override val name: String, val owner: String) : Project()
fun main() {
val data: Project = OwnedProject("kotlinx.coroutines", "kotlin")
println(Json.encodeToString(data))
type
键的值可以使用属性名
@Serializable
@SerialName("owned")
class OwnedProject(override val name: String, val owner: String) : Project()
当集合存在多态时会默认添加一个type
字段
一般情况下推荐使用插件JSON to Kotlin Class
插件生成数据模型. 并且生成带有默认值的数据类, 可以保证后端字段null情况不会覆盖默认值导致空指针
插件使用带有默认值的数据类型
@Serializable
data class Data(var name:String = "", var age:Int = 0)
推荐Json配置
val jsonDecoder = Json {
ignoreUnknownKeys = true
coerceInputValues = true
Json配置
使用Json{}
构建Json实例对象
val format = Json { prettyPrint = true }
@Serializable
data class Project(val name: String, val language: String)
fun main() {
val data = Project("kotlinx.serialization", "Kotlin")
println(format.encodeToString(data))
选项 | 描述 | 默认值 |
---|
prettyPrint: Boolean | 生成排版后的JSON | false |
prettyPrintIndent: String | 指定排版的缩进字符串 | false |
isLenient: Boolean | 允许非双引号包裹键值 (推荐配置) | false |
ignoreUnknownKeys: Boolean | 允许反序列化的数据类缺失字段 (推荐配置) | false |
explicitNulls: Boolean | 反序列化时json不存在的值赋值给数据类null, 序列化时不存在且无默认值的赋值null | false |
useAlternativeNames: Boolean | 是否启用@JsonName注解, 如果不用该注解建议禁用该配置提高性能 | true |
coerceInputValues: Boolean | 空和未知枚举不会覆盖属性默认值 | false |
allowStructuredMapKeys: Boolean | 启用Map序列化. 默认情况是不能序列化Map | false |
allowSpecialFloatingPointValues: Boolean | 允许序列化Double.NaN 这种特殊浮点类型 | false |
classDiscriminator = "#class" | 使用属性名作为值, #class 为自定义ide键名 | |
serializersModule = moduleForDate | | |
encodeDefaults: Boolean | 是否序列化默认值 支持父类的属性(非抽象)序列化 | false |
启用Map序列化(默认情况是不能序列化Map)
val format = Json { allowStructuredMapKeys = true }
@Serializable
data class Project(val name: String)
fun main() {
val map = mapOf(
Project("kotlinx.serialization") to "Serialization",
Project("kotlinx.coroutines") to "Coroutines"
println(format.encodeToString(map))
这样在序列化的JSON中会自动新增一个字段用于描述数据模型的类信息
val format = Json { classDiscriminator = "#class" }
@Serializable
sealed class Project {
abstract val name: String
@Serializable
@SerialName("owned")
class OwnedProject(override val name: String, val owner: String) : Project()
fun main() {
val data: Project = OwnedProject("kotlinx.coroutines", "kotlin")
println(format.encodeToString(data))
允许规定浮点值
val data = Data(Double.NaN)
println(format.encodeToString(data))
启用默认值
设置coerceInputValues
为true, 会在类型为不可空, 但是JSON值为空的情况下采用默认值而非覆盖字段.
同时当出现未知的枚举类型也会使用默认值
字段缺失分为两种
数据类字段比json中少, 使用ignoreUnknownKeys = true
json字段比数据类少
当数据类字段为可空, 则赋值为null. 要求explicitNulls = false
当数据类型字段存在默认值(无论是否为可空), 则使用默认值
val jsonDecoder = Json {
ignoreUnknownKeys = true
explicitNulls = false
JsonElement
使用函数 Json.parseToJsonElement
解析出一个JsonElement对象, 该对象非反序列化
子类 | 描述 |
---|
JsonPrimitive | Kotlin中的原始类型 |
JsonArray | 一个JsonElement的集合 |
JsonObject | 一个JsonElement的Map集合 |
JsonNull | 空类型 |
JsonPrimitive
具备一系列基础类型获取函数, 返回String请调用content
函数.
函数 | 描述 |
---|
jsonPrimitive | 返回原始类型 |
jsonObject | 返回Map |
jsonArray | 返回集合 |
jsonNull | 返回Null |
fun main() {
val element = Json.parseToJsonElement("""
"name": "kotlinx.serialization",
"forks": [{"votes": 42}, {"votes": 9000}, {}]
""")
val sum = element
.jsonObject["forks"]!!
.jsonArray.sumOf { it.jsonObject["votes"]?.jsonPrimitive?.int ?: 0 }
println(sum)
构建JSON
提供顶层DSL函数构建JSON
fun main() {
val element = buildJsonObject {
put("name", "kotlinx.serialization")
putJsonObject("owner") {
put("name", "kotlin")
putJsonArray("forks") {
addJsonObject {
put("votes", 42)
addJsonObject {
put("votes", 9000)
println(element)
KSerializer
KSerializer属于Serialization中的序列者, 一个包含序列化和反序列化的接口规范.
可以通过以下方式创建序列器
绑定数据类
@Serializable(with = ColorAsStringSerializer::class)
class Color(val rgb: Int)
在序列器上指定对象
class Project(val name: String, val language: String)
@Serializer(forClass = Project::class)
object ProjectSerializer
public fun <T> decodeFromJsonElement(deserializer: DeserializationStrategy<T>, element: JsonElement): T
指定当前文件的类使用的序列器
@file:UseSerializers(DateAsLongSerializer::class)
@Serializable
class ProgrammingLanguage(val name: String, val stableReleaseDate: Date)
自动生成序列器
每个被@Serializable
修饰的类都存在一个单例函数serializer()
返回一个KSerializer<T>
序列化
故你不能声明创建一个serializer
函数
原始类型存在默认的序列者: Int.serializer()
public interface SerializationStrategy<in T> {
public val descriptor: SerialDescriptor
public fun serialize(encoder: Encoder, value: T)
public interface DeserializationStrategy<T> {
public val descriptor: SerialDescriptor
public fun deserialize(decoder: Decoder): T
public interface KSerializer<T> : SerializationStrategy<T>, DeserializationStrategy<T> {
override val descriptor: SerialDescriptor
编码和解码
编码和解码 | 描述 |
---|
Encoder | 序列化根接口 |
CompositeEncoder | |
JsonEncoder | 用于JSON序列化 |
Decoder | 反序列化根接口 |
CompositeDecoder | |
JsonDecoder | 用于JSON反序列化 |
Encoder可以使用encodeStructure
来开始手动编码
Decoder内部存在一个循环一直调用decodeElementIndex
开始解码, 遇到CompositeDecoder.DECODE_DONE
时停止循环
如果数据是按照顺序存储的, 我们可以直接使用decodeSequentially
函数来终止循环
override fun deserialize(decoder: Decoder): Color =
decoder.decodeStructure(descriptor) {
var r = -1
var g = -1
var b = -1
if (decodeSequentially()) {
r = decodeIntElement(descriptor, 0)
g = decodeIntElement(descriptor, 1)
b = decodeIntElement(descriptor, 2)
} else while (true) {
when (val index = decodeElementIndex(descriptor)) {
0 -> r = decodeIntElement(descriptor, 0)
1 -> g = decodeIntElement(descriptor, 1)
2 -> b = decodeIntElement(descriptor, 2)
CompositeDecoder.DECODE_DONE -> break
else -> error("Unexpected index: $index")
require(r in 0..255 && g in 0..255 && b in 0..255)
Color((r shl 16) or (g shl 8) or b)
Serialization中定义了很多顶层函数来创建序列者: BuiltinSerializers.kt
序列者 |
---|
PairSerializer |
MapEntrySerializer |
TripleSerializer |
*ArraySerializer |
ListSerializer |
SetSerializer |
MapSerializer |
LongAsStringSerializer |
基本囊括了所有数据类型, 反序列化不需要使用序列者默认支持类型. 枚举序列化和反序列化都不需要多余的处理
使用泛型类型推断可以快速生成对应的序列者
val stringToColorMapSerializer: KSerializer<Map<String, Color>> = serializer()
println(stringToColorMapSerializer.descriptor)
Serialization解析Map, 键永远是字符串, 即使是数字
@Serializable
class Project(val name: String)
fun main() {
val map = mapOf(
1 to Project("kotlinx.serialization"),
2 to Project("kotlinx.coroutines")
println(Json.encodeToString(map))
@Serializable
class Box<T>(val contents: T)
fun main() {
val boxedColorSerializer = Box.serializer(Color.serializer())
println(boxedColorSerializer.descriptor)
泛型数量会导致要求传递给serializer
的参数数量, 每个泛型参数都应该有属于自己的序列者
Json序列化
JsonTransformingSerializer
属于Json解析实现. 如果我们需要自定义解析JSON的序列化器我们可以继承该类实现函数
public abstract class JsonTransformingSerializer<T : Any>(
private val tSerializer: KSerializer<T>
) : KSerializer<T> {
protected open fun transformDeserialize(element: JsonElement): JsonElement = element
protected open fun transformSerialize(element: JsonElement): JsonElement = element
过滤掉某值(默认情况默认值会被过滤)
为了方便使用和节约内存, 解析器一般使用单例对象
fun main() {
val data = Project("kotlinx.serialization", "Kotlin")
println(Json.encodeToString(data))
println(Json.encodeToString(ProjectSerializer, data))
@Serializable
class Project(val name: String, val language: String)
object ProjectSerializer : JsonTransformingSerializer<Project>(Project.serializer()) {
override fun transformSerialize(element: JsonElement): JsonElement =
JsonObject(element.jsonObject.filterNot {
(k, v) -> k == "language" && v.jsonPrimitive.content == "Kotlin"
多态序列化提供一个函数用于返回具体序列化器
object ProjectSerializer : JsonContentPolymorphicSerializer<Project>(Project::class) {
override fun selectDeserializer(element: JsonElement) = when {
"owner" in element.jsonObject -> OwnedProject.serializer()
else -> BasicProject.serializer()
ProtoBuf
protobuf属于比json体积/性能更好的数据格式, 应用同样也比较广泛. 这里介绍如何通过ks快速处理protobuf
implementation "org.jetbrains.kotlinx:kotlinx-serialization-protobuf:1.3.0"
@Serializable
data class Project(val name: String, val language: String)
fun main() {
val data = Project("kotlinx.serialization", "Kotlin")
val bytes = ProtoBuf.encodeToByteArray(data)
println(bytes.toAsciiHexString())
val obj = ProtoBuf.decodeFromByteArray<Project>(bytes)
println(obj)
使用注解@ProtoNumber
来表示
@Serializable
data class Project(
@ProtoNumber(1)
val name: String,
@ProtoNumber(3)
val language: String
fun main() {
val data = Project("kotlinx.serialization", "Kotlin")
val bytes = ProtoBuf.encodeToByteArray(data)
println(bytes.toAsciiHexString())
val obj = ProtoBuf.decodeFromByteArray<Project>(bytes)
println(obj)
Int类型
@Serializable
class Data(
@ProtoType(ProtoIntegerType.DEFAULT)
val a: Int,
@ProtoType(ProtoIntegerType.SIGNED)
val b: Int,
@ProtoType(ProtoIntegerType.FIXED)
val c: Int
fun main() {
val data = Data(1, -2, 3)
println(ProtoBuf.encodeToByteArray(data).toAsciiHexString())
ProtoIntegerType.DEFAULT 使用protobuf中的int*
类型, 针对正数优化
ProtoIntegerType.SIGNED 使用protobuf中的sint*
类型, 针对负数优化
ProtoIntegerType.FIXED 使用protobuf中的fixed*
类型, 固定字节
uintXX
和 sfixedXX
类型暂时不支持