Slide 1

Slide 1 text

Make the objects serializable with kotlinx.serialization Keishin Yokomaku (@KeithYokoma)
 potatotips #67

Slide 2

Slide 2 text

Make the objects serializable with kotlinx.serialization About Me ▸ Keishin Yokomaku ▸ @KeithYokoma: GitHub / Twitter / Qiita / Stack Overflow ▸ Merpay, Inc. / Engineer ▸ Fun: Gymnastics / Cycling / Photography / Motorsport / Camping potatotips #67 2

Slide 3

Slide 3 text

Make the objects serializable with kotlinx.serialization kotlinx.serialization ▸ https://github.com/Kotlin/kotlinx.serialization ▸ Cross-platform / Multi-platform reflectionless serialization for Kotlin ▸ Supports JSON/CBOR/Protobuf formats ▸ Works on Kotlin/JVM, Kotlin/JS and Kotlin/Native potatotips #67 3

Slide 4

Slide 4 text

Make the objects serializable with kotlinx.serialization kotlinx.serialization 101 ▸ 1. Add kotlinx.serialization plugin ▸ 2. Add kotlinx.serialization runtime into dependency ▸ 3. Annotate with @Serializable ▸ 4. Serialize an object with formatter potatotips #67 4

Slide 5

Slide 5 text

Make the objects serializable with kotlinx.serialization 1. Add kotlinx.serialization plugin buildscript {
 
 ext.kotlin_version = '1.3.61'
 
 repositories {
 jcenter()
 }
 
 dependencies {
 classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
 }
 } 5 potatotips #67

Slide 6

Slide 6 text

Make the objects serializable with kotlinx.serialization 1. Add kotlinx.serialization plugin / project_root/build.gradle buildscript {
 
 ext.kotlin_version = '1.3.61'
 
 repositories {
 jcenter()
 }
 
 dependencies {
 classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
 classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
 }
 } 6 potatotips #67

Slide 7

Slide 7 text

Make the objects serializable with kotlinx.serialization 1. Add kotlinx.serialization plugin / module_root/build.gradle apply plugin: "kotlin" dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" } 7 potatotips #67

Slide 8

Slide 8 text

Make the objects serializable with kotlinx.serialization 1. Add kotlinx.serialization plugin / module_root/build.gradle apply plugin: "kotlin" apply plugin: "kotlinx.serialization" dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" } 8 potatotips #67

Slide 9

Slide 9 text

Make the objects serializable with kotlinx.serialization 2. Add kotlinx.serialization runtime into dependency apply plugin: "kotlin" apply plugin: "kotlinx.serialization" dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime: 0.14.0" // not $kotlin_version!!! } 9 potatotips #67

Slide 10

Slide 10 text

Make the objects serializable with kotlinx.serialization 3. Annotate with @Serializable data class SampleScreenState( val familyName: String, val givenName: String, val age: Int ) 10 potatotips #67

Slide 11

Slide 11 text

Make the objects serializable with kotlinx.serialization 3. Annotate with @Serializable @Serializable data class SampleScreenState( val familyName: String, val givenName: String, val age: Int ) 11 potatotips #67

Slide 12

Slide 12 text

Make the objects serializable with kotlinx.serialization 4. Serialize an object with formatter @Serializable data class SampleScreenState( val familyName: String, val givenName: String, val age: Int ) val originalState = SampleScreenState("foo", "bar", 30) 12 potatotips #67

Slide 13

Slide 13 text

Make the objects serializable with kotlinx.serialization 4. Serialize an object with formatter @Serializable data class SampleScreenState( val familyName: String, val givenName: String, val age: Int ) val originalState = SampleScreenState("foo", "bar", 30) val json = Json(JsonConfiguration.Stable) 13 potatotips #67

Slide 14

Slide 14 text

Make the objects serializable with kotlinx.serialization 4. Serialize an object with formatter @Serializable data class SampleScreenState( val familyName: String, val givenName: String, val age: Int ) val originalState = SampleScreenState("foo", "bar", 30) val json = Json(JsonConfiguration.Stable) // {"familyName":"foo","givenName":"bar","age":30} val serialized = json.stringify(originalState) 14 potatotips #67

Slide 15

Slide 15 text

Make the objects serializable with kotlinx.serialization 4. Serialize an object with formatter @Serializable data class SampleScreenState( val familyName: String, val givenName: String, val age: Int ) val originalState = SampleScreenState("foo", "bar", 30) val json = Json(JsonConfiguration.Stable) // {"familyName":"foo","givenName":"bar","age":30} val serialized = json.stringify(originalState) val restoredState =
 json.parse(SampleScreenState::class.serializer(), serialized) 15 potatotips #67

Slide 16

Slide 16 text

Make the objects serializable with kotlinx.serialization 4. Serialize an object with formatter @Serializable data class SampleScreenState( val familyName: String, val givenName: String, val age: Int ) val originalState = SampleScreenState("foo", "bar", 30) val json = Json(JsonConfiguration.Stable) // {"familyName":"foo","givenName":"bar","age":30} val serialized = json.stringify(originalState) val restoredState =
 json.parse(SampleScreenState::class.serializer(), serialized) println(originalState == restoredState) // true 16 potatotips #67

Slide 17

Slide 17 text

Make the objects serializable with kotlinx.serialization kotlinx.serialization extended ▸ Polymorphic serialization for sealed classes ▸ Make non-serializable objects in a library serializable 17 potatotips #67

Slide 18

Slide 18 text

Make the objects serializable with kotlinx.serialization Polymorphic serialization for sealed classes 18 potatotips #67 sealed class SampleType data class FooType(val text: String = "foo") : SampleType() data class BarType(val flag: Boolean = false) : SampleType() @Serializable data class SampleScreenState(val d: SampleType)

Slide 19

Slide 19 text

Make the objects serializable with kotlinx.serialization Polymorphic serialization for sealed classes 19 potatotips #67 @Serializable sealed class SampleType @Serializable data class FooType(val text: String = "foo") : SampleType() @Serializable data class BarType(val flag: Boolean = false) : SampleType() @Serializable data class SampleScreenState(val d: SampleType)

Slide 20

Slide 20 text

Make the objects serializable with kotlinx.serialization Polymorphic serialization for sealed classes 20 potatotips #67 @Serializable sealed class SampleType @Serializable data class FooType(val text: String = "foo") : SampleType() @Serializable data class BarType(val flag: Boolean = false) : SampleType() @Serializable data class SampleScreenState(val d: SampleType) val originalState = SampleScreenState(FooType()) val json = Json(JsonConfiguration.Stable) // {"d":{"type":"com.sample.FooType", "text":"foo"}} val serialized = json.stringify(originalState)

Slide 21

Slide 21 text

Make the objects serializable with kotlinx.serialization Polymorphic serialization for sealed classes: prior to 0.14.0 21 potatotips #67 sealed class SampleType data class FooType(val text: String = "foo") : SampleType() data class BarType(val flag: Boolean = false) : SampleType() @Serializable data class SampleScreenState(val d: SampleType) val json = Json(JsonConfiguration.Stable) // Don’t know how to serialize FooType val serialized = json.stringify(SampleScreenState(FooType()))

Slide 22

Slide 22 text

Make the objects serializable with kotlinx.serialization Polymorphic serialization for sealed classes: prior to 0.14.0 22 potatotips #67 sealed class SampleType @Serializable data class FooType(val text: String = "foo") : SampleType() @Serializable data class BarType(val flag: Boolean = false) : SampleType() @Serializable data class SampleScreenState(@Polymorphic val d: SampleType) val json = Json(JsonConfiguration.Stable) // Still don’t know how to serialize FooType val serialized = json.stringify(SampleScreenState(FooType()))

Slide 23

Slide 23 text

Make the objects serializable with kotlinx.serialization Polymorphic serialization for sealed classes: prior to 0.14.0 23 potatotips #67 sealed class SampleType @Serializable data class FooType(val text: String = "foo") : SampleType() @Serializable data class BarType(val flag: Boolean = false) : SampleType() @Serializable data class SampleScreenState(@Polymorphic val d: SampleType) val sampleTypeModule = SerializersModule { polymorphic { addSubclass() addSubclass() } } val json = Json(JsonConfiguration.Stable, sampleTypeModule) // {"d":{"type":"com.sample.FooType", "text":"foo"}} val serialized = json.stringify(SampleScreenState(FooType()))

Slide 24

Slide 24 text

Make the objects serializable with kotlinx.serialization Make non-serializable objects in a library serializable 24 potatotips #67 // Assume SampleType is in the library and cannot have @Serializable sealed class SampleType data class FooType(val text: String = "foo") : SampleType() data class BarType(val flag: Boolean = false) : SampleType() @Serializable data class SampleScreenState(val d: SampleType) val json = Json(JsonConfiguration.Stable) // Don’t know how to serialize FooType val serialized = json.stringify(SampleScreenState(FooType()))

Slide 25

Slide 25 text

Make the objects serializable with kotlinx.serialization Make non-serializable objects in a library serializable 25 potatotips #67 // Assume SampleType is in the library and cannot have @Serializable sealed class SampleType data class FooType(val text: String = "foo") : SampleType() data class BarType(val flag: Boolean = false) : SampleType() @Serializable data class SampleScreenState(@Polymorphic val d: SampleType) val json = Json(JsonConfiguration.Stable) // Still don’t know how to serialize FooType val serialized = json.stringify(SampleScreenState(FooType()))

Slide 26

Slide 26 text

Make the objects serializable with kotlinx.serialization Make non-serializable objects in a library serializable 26 potatotips #67 // Assume SampleType is in the library and cannot have @Serializable sealed class SampleType data class FooType(val text: String = "foo") : SampleType() data class BarType(val flag: Boolean = false) : SampleType() @Serializable data class SampleScreenState(@Polymorphic val d: SampleType) val sampleTypeModule = SerializersModule { polymorphic { addSubclass() // Need to have serializer for FooType addSubclass() // Need to have serializer for BarType } } val json = Json(JsonConfiguration.Stable, sampleTypeModule) // Still don’t know how to serialize FooType val serialized = json.stringify(SampleScreenState(FooType()))

Slide 27

Slide 27 text

Make the objects serializable with kotlinx.serialization Make non-serializable objects in a library serializable 27 potatotips #67 // Assume SampleType is in the library and cannot have @Serializable sealed class SampleType data class FooType(val text: String = "foo") : SampleType() data class BarType(val flag: Boolean = false) : SampleType() @Serializable data class SampleScreenState(@Polymorphic val d: SampleType) object FooTypeSerializer : KSerializer { /* implementation */ } // define how to serialize FooType object BarTypeSerializer : KSerializer { /* implementation */ } // define how to serialize BarType val sampleTypeModule = SerializersModule { polymorphic { addSubclass(FooTypeSerializer) addSubclass(BarTypeSerializer) } } val json = Json(JsonConfiguration.Stable, sampleTypeModule) // {"d":{"type":"com.sample.FooType", "text":"foo"}} val serialized = json.stringify(SampleScreenState(FooType()))

Slide 28

Slide 28 text

Make the objects serializable with kotlinx.serialization Make non-serializable objects in a library serializable 28 potatotips #67 // Assume SampleType is in the library and cannot have @Serializable sealed class SampleType data class FooType(val text: String = "foo") : SampleType() object FooTypeSerializer : KSerializer { /* implementation */ }

Slide 29

Slide 29 text

Make the objects serializable with kotlinx.serialization Make non-serializable objects in a library serializable 29 potatotips #67 // Assume SampleType is in the library and cannot have @Serializable sealed class SampleType data class FooType(val text: String = "foo") : SampleType() object FooTypeSerializer : KSerializer { override val descriptor =
 object : SerialClassDescImpl(FooType::class.java.name) { init { // remember this element is the first element (index = 0) addElement("text") } } }

Slide 30

Slide 30 text

Make the objects serializable with kotlinx.serialization Make non-serializable objects in a library serializable 30 potatotips #67 // Assume SampleType is in the library and cannot have @Serializable sealed class SampleType data class FooType(val text: String = "foo") : SampleType() object FooTypeSerializer : KSerializer { override val descriptor = // …… override fun deserialize(decoder: Decoder): FooType { val input = decoder.beginStructure(descriptor) val text = input.decodeStringElement(descriptor, 0) input.endStructure(descriptor) return FooType(text) } }

Slide 31

Slide 31 text

Make the objects serializable with kotlinx.serialization Make non-serializable objects in a library serializable 31 potatotips #67 // Assume SampleType is in the library and cannot have @Serializable sealed class SampleType data class FooType(val text: String = "foo") : SampleType() object FooTypeSerializer : KSerializer { override val descriptor = // …… override fun serialize(encoder: Encoder, obj: FooType) { val output = encoder.beginStructure(descriptor) val text = output.encodeStringElement(descriptor, 0, obj.text) output.endStructure(descriptor) } }

Slide 32

Slide 32 text

Make the objects serializable with kotlinx.serialization Drawback 32 potatotips #67 @Serializable sealed class SampleType @Serializable data class FooType(val type: String = "foo") : SampleType() @Serializable data class BarType(val flag: Boolean = false) : SampleType() @Serializable data class SampleScreenState(val d: SampleType) val originalState = SampleScreenState(FooType()) val json = Json(JsonConfiguration.Stable) // {"d":{"type":"com.sample.FooType", "type":"foo"}} val serialized = json.stringify(originalState) // cannot restore because the JSON parser will take the latter "type" value val restored = json.parse(SampleScreenState::class.serializer(), serialized)

Slide 33

Slide 33 text

Make the objects serializable with kotlinx.serialization Workaround #1: change the key for FQCN 33 potatotips #67 @Serializable sealed class SampleType @Serializable data class FooType(val type: String = "foo") : SampleType() @Serializable data class BarType(val flag: Boolean = false) : SampleType() @Serializable data class SampleScreenState(val d: SampleType) val originalState = SampleScreenState(FooType()) val json = Json(JsonConfiguration.Stable.copy(classDiscriminator = "kclass")) // {"d":{"kclass":"com.sample.FooType", "type":"foo"}} val serialized = json.stringify(originalState) val restored = json.parse(SampleScreenState::class.serializer(), serialized)

Slide 34

Slide 34 text

Make the objects serializable with kotlinx.serialization Workaround #2: Use array polymorphism 34 potatotips #67 @Serializable sealed class SampleType @Serializable data class FooType(val type: String = "foo") : SampleType() @Serializable data class BarType(val flag: Boolean = false) : SampleType() @Serializable data class SampleScreenState(val d: SampleType) val originalState = SampleScreenState(FooType()) val json = Json(JsonConfiguration.Stable.copy(useArrayPolymorphism = true)) // {"d":["com.sample.FooType", {"type":"foo"}]} val serialized = json.stringify(originalState) val restored = json.parse(SampleScreenState::class.serializer(), serialized)

Slide 35

Slide 35 text

Make the objects serializable with kotlinx.serialization Wrap up: kotlinx.serialization ▸ Cross-platform / Multi-platform reflectionless serialization for Kotlin ▸ Supports JSON/CBOR/Protobuf formats ▸ Works on Kotlin/JVM, Kotlin/JS and Kotlin/Native ▸ Compiler help us making the object serializable :) potatotips #67 35

Slide 36

Slide 36 text

Make the objects serializable with kotlinx.serialization Keishin Yokomaku (@KeithYokoma)
 potatotips #67