DESIGN OF KOTLINX SERIALIZATION LEONID STARTSEV sandwwraith Copenhagen





























































![How KSerializer looks inside? . deserialize call begin. Structure decode. Element. Index decode[Type]Element 62 How KSerializer looks inside? . deserialize call begin. Structure decode. Element. Index decode[Type]Element 62](https://slidetodoc.com/presentation_image_h/c99ee47181ca06eac591a92824a7f05b/image-62.jpg)
![How KSerializer looks inside? . deserialize call begin. Structure decode. Element. Index decode[Type]Element end. How KSerializer looks inside? . deserialize call begin. Structure decode. Element. Index decode[Type]Element end.](https://slidetodoc.com/presentation_image_h/c99ee47181ca06eac591a92824a7f05b/image-63.jpg)



































- Slides: 98
DESIGN OF KOTLINX. SERIALIZATION LEONID STARTSEV @sandwwraith Copenhagen Denmark
At a first glance, it looks like a regular JSON library 2
At a first glance, it looks like a regular JSON library val json = Json(Json. Configuration. Stable) 3
At a first glance, it looks like a regular JSON library val json = Json(Json. Configuration. Stable) json. parse(. . . ) json. stringify(. . . ) 4
Why do we need yet another json parser? 5
Why do we need yet another json parser? These all are Java libraries 6
Multiplatform @Serializable data class Session( val id: String, val is. Service. Session: Boolean, val is. Plenum. Session: Boolean, val question. Answers: List<Question. Answer>, val speakers: List<String>, @Serial. Name("description") val description. Text: String? , val starts. At: String? , val title: String, val ends. At: String? , val category. Items: List<Int>, val room. Id: Int? ) 7
Kotlin-oriented data class Data( val data: String, val optional. Data: String = "empty" ) const val input. String = """{"data": "Foo"}""" 8
Kotlin-oriented data class Data( val data: String, val optional. Data: String = "empty" ) val data = Gson(). from. Json(input. String, Data: : class. java) // Data(data=Foo, optional. Data=null) Null for non-nullable type, definitely an error 9
Kotlin-oriented @Serializable data class Data( val data: String, val optional. Data: String = "empty" ) val data = Json. parse(Data. serializer(), input. String) // Data(data=Foo, optional. Data=empty) OK! 10
Explicit @Serializable data class Data( val data: String, val optional. Data: String = "empty" ) 11
Explicit @Serializable data class Data( val data: String, val optional. Data: String = "empty" ) val serializer: KSerializer<Data> = Data. Companion. serializer() 12
Explicit @Serializable data class Data( val data: String, val optional. Data: String = "empty" ) val serializer: KSerializer<Data> = Data. Companion. serializer() val data = Json. parse(Data. serializer(), input. String) 13
Implicit requires opt-in val data = Json. parse<List<Data>>(input. String) 14
Implicit requires opt-in val data = Json. parse<List<Data>>(input. String) This declaration is experimental and its usage must be marked with @Use. Experimental(Implicit. Reflection. Serializer: : class) 15
Implicit requires opt-in val data = Json. parse<List<Data>>(input. String) This declaration is experimental and its usage must be marked with @Use. Experimental(Implicit. Reflection. Serializer: : class) ● Doesn’t work on Native ● Doesn’t work for collections 16 https: //github. com/Kotlin/kotlinx. serialization/blob/master/runtime/common. Main/src/kotlinx/serialization/Serial. Implicits. kt#L 8
Concise val data: List<Data> = Gson(). from. Json( ) 17
Concise val data: List<Data> = Gson(). from. Json( input. Generic. String, (object: Type. Token<List<Data>>() {}). type ) A lot of ceremony 18
Concise val data: List<Data> = Json. parse( Data. serializer(). list, ) input. Generic. String Short and uniform 19 https: //github. com/Kotlin/kotlinx. serialization/blob/master/docs/runtime_usage. md#obtaining-serializers
Explicit & concise & compiletime safe @Serializable data class Complex. Data( val description: String, val data: Complex. Class ) 20
Explicit & concise & compiletime safe @Serializable data class Complex. Data( val description: String, val data: Complex. Class ) Serializer for type ‘Complex. Class’ has not been found IDE diagnostic! (from Kotlin 1. 3. 50) 21
Formats are pluggable val string. Data = Json. stringify(Data. serializer(), data) val bytes. Data 22 = Proto. Buf. dump (Data. serializer(), data)
Formats are pluggable val string. Data = Json. stringify(Data. serializer(), data) val bytes. Data = Proto. Buf. dump (Data. serializer(), data) Different formats, same API 23
Difference between serializer and format 24
Difference between serializer and format Stream of primitives Kotlin class Serialization by serializer 25 Output stream Encoding by format
Difference between serializer and format ● Optionals handling ● Class metadata Stream of primitives Kotlin class Serialization by serializer 26 Output stream Encoding by format
Difference between serializer and format ● Optionals handling ● Class metadata Stream of primitives Kotlin class Serialization by serializer 27 ● Text or bytes ● Versioning ● Schema or schemaless Output stream Encoding by format
Formats have their specific configuration val json = Json(Json. Configuration( encode. Defaults = false, unquoted = true, pretty. Print = true )) 28
Formats have their specific configuration val json = Json(Json. Configuration( encode. Defaults = false, unquoted = true, pretty. Print = true )) val protobuf = Proto. Buf() 29
Part II Behind the scenes
Why @Serializable and apply plugin: ‘. . . ’? @Serializable data class Data( val data: String, val optional. Data: String = "empty" ) 31
Why @Serializable and apply plugin: ‘. . . ’? @Serializable data class Data( val data: String, val optional. Data: String = "empty" ) // @Serializable data class Data( val data: String, val optional. Data: String = "empty" ) 32
Why @Serializable and apply plugin: ‘. . . ’? apply plugin: 'kotlin-multiplatform' //apply plugin: 'kotlinx-serialization' 33
Why @Serializable and apply plugin: ‘. . . ’? apply plugin: 'kotlin-multiplatform' apply plugin: 'kotlinx-serialization' Kotlin serialization consists of acompiler plugin, which automatically produces visitor code for classes, and runtime library, which uses generated code to serialize objects without reflection. 34
What is a compiler plugin, anyway? 35
What is a compiler plugin, anyway? MAGIC 36
What is a compiler plugin, anyway? Frontend (parsing, resolution) JVM Backend 37 JS Backend Very simple scheme of Kotlin compiler Native Backend
Serialization plugin Adds. serializer() function Frontend (parsing, resolution) JVM Backend 38 JS Backend Not so simple scheme of Kotlin compiler Native Backend
Serialization plugin Adds. serializer() function Frontend (parsing, resolution) Additional code generation JVM Backend 39 JS Backend Not so simple scheme of Kotlin compiler Native Backend
So we are writing a serializer, right? 40
Difference between serializer and format Stream of primitives Kotlin class Serialization by serializer 41 Output stream Encoding by format
Which methods does Encoder have? interface Encoder { fun encode. Not. Null. Mark() fun encode. Null() fun fun fun encode. Unit() encode. Boolean(value: Boolean) encode. Byte(value: Byte) encode. Short(value: Short) encode. Int(value: Int) encode. Long(value: Long) encode. Float(value: Float) encode. Double(value: Double) encode. Char(value: Char) fun encode. String(value: String) fun encode. Enum(enum. Description: Enum. Descriptor, ordinal: Int) } 42 fun <T : Any? > encode. Serializable. Value(serializer: Serialization. Strategy<T>, value: T)
Which methods does Encoder have? interface Encoder { fun encode. Not. Null. Mark() fun encode. Null() fun fun fun encode. Unit() encode. Boolean(value: Boolean) encode. Byte(value: Byte) encode. Short(value: Short) encode. Int(value: Int) encode. Long(value: Long) encode. Float(value: Float) encode. Double(value: Double) encode. Char(value: Char) One function for each ‘primitive’ data type fun encode. String(value: String) fun encode. Enum(enum. Description: Enum. Descriptor, ordinal: Int) } 43 fun <T : Any? > encode. Serializable. Value(serializer: Serialization. Strategy<T>, value: T)
Transform to single value @Serializable data class Color(val r: Int, val g: Int, val b: Int) 44
Transform to single value @Serializable data class Color(val r: Int, val g: Int, val b: Int) "#00 FFAA" 45
Transform to single value object Single. Color. Serializer : KSerializer<Color> { override fun serialize(encoder: Encoder, obj: Color) { } } 46
Transform to single value object Single. Color. Serializer : KSerializer<Color> { override fun serialize(encoder: Encoder, obj: Color) { val code: String = obj. r. to. Hex. Code() + obj. g. to. Hex. Code() + obj. b. to. Hex. Code() } } 47
Transform to single value object Single. Color. Serializer : KSerializer<Color> { override fun serialize(encoder: Encoder, obj: Color) { val code: String = obj. r. to. Hex. Code() + obj. g. to. Hex. Code() + obj. b. to. Hex. Code() encoder. encode. String("#$code") } } 48
Transform from multiple values """{"red": 255, "green": 128, "blue": 0}""" 49
Transform from multiple values """{"red": 255, "green": 128, "blue": 0}""" @Serializable data class Color(val argb: Int) 50
Transform from multiple values override fun deserialize(decoder: Decoder): Color { var result = 0 result += decoder. decode. Int() shl 16 result += decoder. decode. Int() shl 8 result += decoder. decode. Int() shl 0 return Color(result) } 51
Transform from multiple values override fun deserialize(decoder: Decoder): Color { var result = 0 result += decoder. decode. Int() shl 16 result += decoder. decode. Int() shl 8 result += decoder. decode. Int() shl 0 return Color(result) } We don’t know whether ‘red’ goes first or not 52
How to handle structured data interface Composite. Encoder { fun encode. Int. Element(desc: Serial. Descriptor, index: Int, value: Int). . . fun encode. String. Element(desc: Serial. Descriptor, index: Int, value: String) } Containing declaration 53 Position in structure https: //github. com/Kotlin/KEEP/blob/master/proposals/extensions/serialization. md#encoder-interfaces
Transform from multiple values object Multiple. Color. Serializer : KSerializer<Color> { override fun deserialize(decoder: Decoder): Color { var result = 0 } 54 } return Color(result)
Transform from multiple values object Multiple. Color. Serializer : KSerializer<Color> { override fun deserialize(decoder: Decoder): Color { var result = 0 val decoder: Composite. Decoder = decoder. begin. Structure(descriptor) decoder. end. Structure(descriptor) return Color(result) } } 55
Transform from multiple values override fun deserialize(decoder: Decoder): Color { var result = 0 val decoder = decoder. begin. Structure(descriptor) while (true) { when (val i = decoder. decode. Element. Index(descriptor)) { } } 56 } decoder. end. Structure(descriptor) return Color(result)
Transform from multiple values override fun deserialize(decoder: Decoder): Color { var result = 0 val decoder = decoder. begin. Structure(descriptor) } 57 loop@ while (true) { when (val i = decoder. decode. Element. Index(descriptor)) { 0 -> result += decoder. decode. Int. Element(descriptor, i) shl 16 1 -> result += decoder. decode. Int. Element(descriptor, i) shl 8 2 -> result += decoder. decode. Int. Element(descriptor, i) shl 0 READ_DONE -> break@loop } } decoder. end. Structure(descriptor) return Color(result)
Difference between serializer and format Stream of primitives Kotlin class Serialization by serializer Can be auto-generated! 58 Output stream Encoding by format
How KSerializer looks inside? @Serializable data class Data( val data: String, val optional. Data: String = "empty" ) 59
How KSerializer looks inside? 60
How KSerializer looks inside? 61 https: //github. com/Kotlin/KEEP/blob/master/proposals/extensions/serialization. md#code-generation
How KSerializer looks inside? . deserialize call begin. Structure decode. Element. Index decode[Type]Element 62
How KSerializer looks inside? . deserialize call begin. Structure decode. Element. Index decode[Type]Element end. Structure 63 Constructor call
Serial. Descriptor 64
Serial. Descriptor @Serializable data class Data( val data: String, val optional. Data: String = "empty" ) 65
Serial. Descriptor @Serializable data class Data( val data: String, val optional. Data: String = "empty" ) name 66 "com. myapp. Data"
Serial. Descriptor @Serializable data class Data( val data: String, val optional. Data: String = "empty" ) Contains useful meta information 67 name "com. myapp. Data" elements[0]. name "data" elements[1]. name "optional. Data" elements[1]. is. Optional true
Part III Diving into encoding
Format interfaces don’t tell you anything about… format override fun encode. Int(value: Int) = ? ? ? override fun encode. String(value: String) = ? ? ? 69
Serializable = Parcelable? class Parcelizer(val bundle: Bundle): Named. Value. Encoder() { } 70
Serializable = Parcelable? class Parcelizer(val bundle: Bundle): Named. Value. Encoder() { override fun encode. Tagged. Int(tag: String, value: Int) { } override fun encode. Tagged. String(tag: String, value: String) { } } 71
Serializable = Parcelable class Parcelizer(val bundle: Bundle): Named. Value. Encoder() { override fun encode. Tagged. Int(tag: String, value: Int) { bundle. put. Int(tag, value) } override fun encode. Tagged. String(tag: String, value: String) { bundle. put. String(tag, value) } } 72
Serializable = Multiplatform Parcelable expect class Bundle actual typealias Bundle = android. os. Bundle class Parcelizer(val bundle: Bundle): Named. Value. Encoder() { override fun encode. Tagged. Int(tag: String, value: Int) { bundle. put. Int(tag, value) } override fun encode. Tagged. String(tag: String, value: String) { bundle. put. String(tag, value) } } 73
You even don’t have to store values anywhere 74
You even don’t have to store values anywhere val digest = Message. Digest. get. Instance("MD 5") 75
You even don’t have to store values anywhere val digest = Message. Digest. get. Instance("MD 5") class Hasher(val digest: Message. Digest): Element. Value. Encoder() { } 76
You even don’t have to store values anywhere val digest = Message. Digest. get. Instance("MD 5") class Hasher(val digest: Message. Digest): Element. Value. Encoder() { override fun encode. Byte(value: Byte) { digest. update(value) } override fun encode. String(value: String) { digest. update(value. to. Byte. Array()) } } 77
ORM done wrong class Statement. Serializer(val stmt: Prepared. Statement): Element. Value. Encoder() { } 78
ORM done wrong class Statement. Serializer(val stmt: Prepared. Statement): Element. Value. Encoder() { override fun encode. String(value: String) { stmt. set. String(i++, value) } } 79
ORM done wrong class Statement. Serializer(val stmt: Prepared. Statement): Element. Value. Encoder() { override fun encode. String(value: String) { stmt. set. String(i++, value) } } class Result. Set. Deserializer(private val rs: Result. Set): Named. Value. Decoder() { } 80
ORM done wrong class Statement. Serializer(val stmt: Prepared. Statement): Element. Value. Encoder() { override fun encode. String(value: String) { stmt. set. String(i++, value) } } class Result. Set. Deserializer(private val rs: Result. Set): Named. Value. Decoder() { override fun decode. Tagged. String(tag: String): String { return rs. get. String(tag) } } 81
Serial. Descriptor @Serializable data class Data( val data: String, val optional. Data: String = "empty" ) Contains useful meta information 82 name "com. myapp. Data" elements[0]. name "data" elements[1]. name "optional. Data" elements[1]. is. Optional true
Serial. Descriptor @Serializable data class Generic. Data<T>( val description: String, val content: T ) 83
Serial. Descriptor @Serializable data class Generic. Data<T>( val description: String, val content: T ) Generic. Data. serializer()). descriptor. dump() 84
Serial. Descriptor Generic. Data content data 85 description optional. Data
Serial. Descriptor Generic. Data com. myapp. Generic. Data { description : kotlin. String content : com. myapp. Data { data : kotlin. String optional. Data : kotlin. String } } content data 86 description optional. Data
JSON Schema 87
JSON Schema fun Json. Schema(descriptor: Serial. Descriptor): Json. Object { } 88
JSON Schema fun Json. Schema(descriptor: Serial. Descriptor): Json. Object { descriptor. element. Descriptors(). for. Each. Indexed { index, child -> } } 89
JSON Schema fun Json. Schema(descriptor: Serial. Descriptor): Json. Object { } 90 descriptor. element. Descriptors(). for. Each. Indexed { index, child -> val element. Name = descriptor. get. Element. Name(index) properties[element. Name] = Json. Schema(child) }
JSON Schema fun Json. Schema(descriptor: Serial. Descriptor): Json. Object { descriptor. element. Descriptors(). for. Each. Indexed { index, child -> val element. Name = descriptor. get. Element. Name(index) properties[element. Name] = Json. Schema(child) } } 91 val object. Data = mutable. Map. Of( "description" to Json. Literal(descriptor. name), "properties" to Json. Object(properties) ) return Json. Object(object. Data) https: //github. com/Kotlin/kotlinx. serialization/blob/master/examples/example-visitors/src/main/kotlinx/serialization/schema/Json. Schema. kt
@Serializable data class Data(val data: String, val optional. Data: String = "empty") { 92 } "description": "com. myapp. Data", "properties": { "data": { "description": "kotlin. String" }, "optional. Data": { "description": "kotlin. String" } }
Roadmap
Roadmap ✓ Core ✓ Meta information in descriptors ✓ Multiplatform polymorphism ✓ IDE diagnostics ✓ Objects and sealed classes ✓ Done
Roadmap ✓ Core ✓ Done ✓ Meta information in descriptors ✓ Multiplatform polymorphism ✓ IDE diagnostics ✓ Objects and sealed classes ➢ Inline classes ➢ In progress
Roadmap ✓ Core ✓ Done ✓ Meta information in descriptors ✓ Multiplatform polymorphism ✓ IDE diagnostics ✓ Objects and sealed classes ➢ Inline classes ➢ In progress IO Streaming & Performance tuning Planned
Roadmap ✓ Core ✓ Done ✓ Meta information in descriptors ✓ Multiplatform polymorphism ✓ IDE diagnostics ✓ Objects and sealed classes ➢ Inline classes ➢ In progress IO Streaming & Performance tuning Planned Out of experimental Kotlin 1. 4 Community driven formats
THANK YOU AND REMEMBER TO VOTE https: //github. com/Kotlin/ kotlinx. serialization Leonid Startsev@sandwwraith #Kotlin. Conf