Share
Embed
Transcript
36 slides
-
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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 -
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) -
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) -
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) -
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())) -
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())) -
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<SampleType> { addSubclass<FooType>() addSubclass<BarType>() } } val json = Json(JsonConfiguration.Stable, sampleTypeModule) // {"d":{"type":"com.sample.FooType", "text":"foo"}} val serialized = json.stringify(SampleScreenState(FooType())) -
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())) -
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())) -
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<SampleType> { addSubclass<FooType>() // Need to have serializer for FooType addSubclass<BarType>() // 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())) -
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<FooType> { /* implementation */ } // define how to serialize FooType object BarTypeSerializer : KSerializer<BarType> { /* implementation */ } // define how to serialize BarType val sampleTypeModule = SerializersModule { polymorphic<SampleType> { addSubclass(FooTypeSerializer) addSubclass(BarTypeSerializer) } } val json = Json(JsonConfiguration.Stable, sampleTypeModule) // {"d":{"type":"com.sample.FooType", "text":"foo"}} val serialized = json.stringify(SampleScreenState(FooType())) -
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<FooType> { /* implementation */ } -
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<FooType> { override val descriptor = object : SerialClassDescImpl(FooType::class.java.name) { init { // remember this element is the first element (index = 0) addElement("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<FooType> { 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) } } -
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<FooType> { 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) } } -
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) -
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) -
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) -
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