Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Make the objects serializable with kotlinx.serialization

Make the objects serializable with kotlinx.serialization

Keishin Yokomaku

December 17, 2019
Tweet

More Decks by Keishin Yokomaku

Other Decks in Technology

Transcript

  1. 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
  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
  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
  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
  5. 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
  6. 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
  7. 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
  8. 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
  9. 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
  10. 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
  11. 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
  12. 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
  13. 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
  14. 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
  15. 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
  16. 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
  17. 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)
  18. 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)
  19. 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)
  20. 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()))
  21. 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()))
  22. 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()))
  23. 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()))
  24. 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()))
  25. 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()))
  26. 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()))
  27. 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 */ }
  28. 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") } } }
  29. 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) } }
  30. 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) } }
  31. 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)
  32. 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)
  33. 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)
  34. 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