添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

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 // JSON和数据模型字段可以不匹配
      coerceInputValues = true // 如果JSON字段是Null则使用默认值
    

    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生成排版后的JSONfalse
    prettyPrintIndent: String指定排版的缩进字符串false
    isLenient: Boolean允许非双引号包裹键值 (推荐配置)false
    ignoreUnknownKeys: Boolean允许反序列化的数据类缺失字段 (推荐配置)false
    explicitNulls: Boolean反序列化时json不存在的值赋值给数据类null, 序列化时不存在且无默认值的赋值nullfalse
    useAlternativeNames: Boolean是否启用@JsonName注解, 如果不用该注解建议禁用该配置提高性能true
    coerceInputValues: Boolean空和未知枚举不会覆盖属性默认值false
    allowStructuredMapKeys: Boolean启用Map序列化. 默认情况是不能序列化Mapfalse
    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值为空的情况下采用默认值而非覆盖字段.

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

    字段缺失分为两种

  • 数据类字段比json中少, 使用ignoreUnknownKeys = true
  • json字段比数据类少
  • 当数据类字段为可空, 则赋值为null. 要求explicitNulls = false
  • 当数据类型字段存在默认值(无论是否为可空), 则使用默认值
  • val jsonDecoder = Json {
      ignoreUnknownKeys = true
      explicitNulls = false
    

    JsonElement

    使用函数 Json.parseToJsonElement解析出一个JsonElement对象, 该对象非反序列化

    子类描述
    JsonPrimitiveKotlin中的原始类型
    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)
    

    在序列器上指定对象

    // NOT @Serializable
    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()) { // 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()
    

    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*类型, 固定字节
  • uintXXsfixedXX 类型暂时不支持

    分类:
    Android