千呼万唤始出来,Kotlin官方序列化库终相见(二)

1,389 阅读3分钟

原生类型

  Kotlin序列化具有以下十个原生类型,Boolean, Byte, Short, Int, Long, Float, Double, Char, String, and enums。Kotlin序列化中的其他类型是复合的,由这些原始值组成。 原始值可以直接用来序列化。

  算了,你们还是直接看原始文档吧,没啥好说的,都是些解释性文字,很好理解:github.com/Kotlin/kotl…

  我想说的,是关于自定义序列化器。

  像JSON这样的格式可以控制将对象编码为特定的输出字节,但是如何将对象分解为其组成属性由序列化程序控制。到目前为止,我们一直在通过使用@Serializable注释或使用“内置的类”中显示的内置序列化器来使用自动派生的序列化器。

从简单例子开始

@Serializable
class Color(val rgb: Int)

fun main() {
    val green = Color(0x00ff00)
    println(Json.encodeToString(green))//{"rgb":65280}
    val colorSerializer: KSerializer<Color> = Color.serializer()
    println(colorSerializer.descriptor)//Color(rgb: kotlin.Int)
}

每个标有@Serializable的类(例如上一个示例中的Color类)都将获取由Kotlin序列化编译器插件自动生成的KSerializer接口的实例。我们可以使用类的伴随对象上的.serializer()函数检索此实例。我们可以检查其描述序列化类结构的描述符属性。

对于泛型类,如“泛型类”部分中显示的Box类,自动生成的.serializer()函数接受的参数与相应类中的类型参数一样多。这些参数的类型为KSerializer,因此在为通用类构造序列化器的实例时必须提供实际类型参数的序列化器

@Serializable           
@SerialName("Box")
class Box<T>(val contents: T)    

fun main() {
    val boxedColorSerializer = Box.serializer(Color.serializer())
    println(boxedColorSerializer.descriptor)//Box(contents: Color)
}

fun main() {
    val intSerializer: KSerializer<Int> = Int.serializer()
    println(intSerializer.descriptor)
}

手写复合序列化器

  在某些情况下,替代解决方案不适合。也许我们想要避免额外分配对性能的影响,或者我们想要针对所得串行表示形式的一组可配置/动态属性。在这些情况下,我们需要手动编写一个类序列化器,以模仿生成的序列化器的行为。

  让我们逐一介绍它。首先,使用buildClassSerialDescriptor构建器定义描述符。构建器DSL中的element函数根据类型自动为相应字段获取序列化程序。元素的顺序很重要。它们从零开始索引。

  然后,我们使用encodeStructure DSL编写序列化函数,该DSL提供对CompositeEncoder块中的访问。 Encoder和CompositeEncoder之间的区别在于后者具有与前者的encodeXxx函数相对应的encodeXxxElement函数。必须以与描述符中相同的顺序调用它们。

  最复杂的代码是反序列化功能。它必须支持可以以任意顺序解码属性的格式,例如JSON。它从对encodeStructure的调用开始,以访问CompositeDecoder。在其中,我们编写了一个循环,该循环反复调用decodeElementIndex来解码下一个元素的索引,然后在本示例中使用decodeIntElement解码相应的元素,最后在遇到CompositeDecoder.DECODE_DONE时终止循环。

object ColorAsObjectSerializer : KSerializer<Color> {
    override val descriptor: SerialDescriptor =
        buildClassSerialDescriptor("Color") {
            element<Int>("r")
            element<Int>("g")
            element<Int>("b")
        }
    override fun serialize(encoder: Encoder, value: Color) =
    encoder.encodeStructure(descriptor) {
        encodeIntElement(descriptor, 0, (value.rgb shr 16) and 0xff)
        encodeIntElement(descriptor, 1, (value.rgb shr 8) and 0xff)
        encodeIntElement(descriptor, 2, value.rgb and 0xff)
    }
    override fun deserialize(decoder: Decoder): Color =
        decoder.decodeStructure(descriptor) {
            var r = -1var g = -1var b = -1while (true) {
                when (val index = decodeElementIndex(descriptor)) {
                    0 -> r = decodeIntElement(descriptor, 0)
                    1 -> g = decodeIntElement(descriptor, 1)
                    2 -> b = decodeIntElement(descriptor, 2)
                    CompositeDecoder.DECODE_DONE -> breakelse -> 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)
        }
}


@Serializable(with = ColorAsObjectSerializer::class)
data class Color(val rgb: Int)

fun main() {
    val color = Color(0x00ff00)
    val string = Json.encodeToString(color) 
    println(string)//{"r":0,"g":255,"b":0}
    require(Json.decodeFromString<Color>(string) == color)
}  

下面是我模仿官方例子自己写的变种,重点是类中的实际属性可以与Json串中类型不同,反序列化时能还原即可。

@Serializable(with = ColorHexSerializer::class)
data class Color(val rgb: Int)

object ColorHexSerializer: KSerializer<Color> {
    override fun deserialize(decoder: Decoder): Color =
        decoder.decodeStructure(descriptor){
            var rgb = 0xFFFFFF
            if (decodeSequentially()){
                rgb = decodeStringElement(descriptor, 0).substring(2).toInt(16)
            }else{
                while (true) {
                    when (val index = decodeElementIndex(descriptor)) {
                        0 -> rgb = decodeStringElement(descriptor, 0).substring(2).toInt(16)
                        CompositeDecoder.DECODE_DONE -> break
                        else -> error("Unexpected index: $index")
                    }
                }
            }
            Color(rgb)
        }

    override val descriptor: SerialDescriptor =
        buildClassSerialDescriptor("SerialExample.Color"){
            element<Int>("rgb")
        }

    override fun serialize(encoder: Encoder, value: Color) {
        encoder.encodeStructure(descriptor){
            encodeStringElement(descriptor,0, "0x"+value.rgb.toString(16).padStart(6,'0'))
        }
    }

}

fun main() {
//    val stringToColorMapSerializer: KSerializer<Map<String, SerialExample.Color>> = MapSerializer(String.serializer(),SerialExample.Color.serializer())
//    println(stringToColorMapSerializer.descriptor)

    val red = Color(0xff0000)
    println(Json.encodeToString(red))

    val red2 = Json.decodeFromString<Color>("""{"rgb":"0xff0000"}""")

    println(red.rgb)
    println(red2.rgb)
    println(red == red2)
    //{"rgb":"0xff0000"} 
    //16711680 
    //16711680 
    //true

}

颜色值实际是用Int保存的,但是打印出来的Json字符串是用十六进制字符串表示的,更便于阅读,反序列化时可以还原回Int。