Kotlin最强解析库 – kotlin-serialization 编写网络请求转换器

Serialization是JetBrains开源的解析库, 具备多平台和支持多解析格式, 也是Kotlin上最强大的序列化解析库没有之一

文档:

Serialization独有优势

  1. 非空校验/非空覆盖, 不会覆盖构造参数默认值, 解决后端返回null覆盖字段问题
  2. 解决泛型擦除问题, 直接序列化/反序列化List/Map/Pair等
  3. 非反射高性能
  4. 支持多种格式(ProtoBuf/CBOR/自定义)
  5. 注解数据类自动生成序列者, 手动构造序列者
  6. 代码简洁优雅
  7. 动态解析

Serialization由于其特殊性, 目前只有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.2.0"
复制代码

JSON使用

关键类
Json 单例对象, 用于解析JSON的配置
Json.Default 默认配置的单例对象

函数

Json的函数分为内联泛型函数/普通泛型函数.

  1. 内联泛型函数具备自动解析对象类型. 一般情况直接使用该函数即可
  2. 普通泛型要求指定解析器. 应对于解析封装
函数 描述
encodeToString 序列化到字符串
encodeToJsonElement 序列化到JsonElement
decodeFromString 反序列化自字符串
decodeFromJsonElement 反序列化自JsonElement

示例

// 序列化
val json:String = Json.encodeToString(Model("彭于晏", 23))

// 反序列化
val json = """{"name":"彭于晏","age":33}"""
val model = Json.decodeFromString<Model>(json)
复制代码

序列化

  1. 仅属性支持序列化(即包含setter和getter)
  2. 默认值不会被序列化, 即使是可空属性
@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))
}
复制代码
  1. 私有构造函数, 暴露其他构造函数, 这样Serialization会序列化暴露出来的构造函数属性
  2. 当数据类字段比JSON多出属性会序列化失败, 除非多出的属性存在默认值(@Required则强制要求JSON匹配字段)
  3. 序列化时如果存在循环结构字段, 会导致堆栈溢出, 建议你忽略排除该字段
  4. 当JSON字段覆盖属性值时, 属性值的默认值为一个函数, 该函数不会被调用
  5. 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配置

使用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
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" } // key

@Serializable
sealed class Project {
    abstract val name: String
}
            
@Serializable         
@SerialName("owned") // value
class OwnedProject(override val name: String, val owner: String) : Project()

fun main() {
    val data: Project = OwnedProject("kotlinx.coroutines", "kotlin")
    println(format.encodeToString(data))
  	// {"#class":"owned","name":"kotlinx.coroutines","owner":"kotlin"}
}  
复制代码

允许规定浮点值

val data = Data(Double.NaN)
println(format.encodeToString(data))
// {"value":NaN}
复制代码

启用默认值

设置coerceInputValues为true, 会在类型为不可空, 但是JSON值为空的情况下采用默认值而非覆盖字段.

同时当出现未知的枚举类型也会使用默认值

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中的序列者, 一个包含序列化和反序列化的接口规范.

可以通过以下方式创建序列器

  1. 绑定数据类

    @Serializable(with = ColorAsStringSerializer::class)
    class Color(val rgb: Int)
    复制代码
  2. 在序列器上指定对象

    // NOT @Serializable
    class Project(val name: String, val language: String)
    
    @Serializer(forClass = Project::class)
    object ProjectSerializer
    // 该序列器没有任何代码逻辑就可以使用
    复制代码
  3. 函数参数

    public fun <T> decodeFromJsonElement(deserializer: DeserializationStrategy<T>, element: JsonElement): T
    复制代码
  4. 指定当前文件的类使用的序列器

    @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()) { // sequential decoding protocol
                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)) // using plugin-generated serializer
    println(Json.encodeToString(ProjectSerializer, data)) // using custom serializer
}

@Serializable
class Project(val name: String, val language: String)

object ProjectSerializer : JsonTransformingSerializer<Project>(Project.serializer()) {
    override fun transformSerialize(element: JsonElement): JsonElement =
        // 如果键 "language" 的值为 "Kotlin" 则过滤掉
        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()
    }
}
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享