Make the objects serializable with kotlinx.serialization

Make the objects serializable with kotlinx.serialization

Bbe9718bebdafbdc8dabbe3cadf1bc46?s=128

Keishin Yokomaku

December 17, 2019
Tweet

Transcript

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

    #67
  2. 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
  3. 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
  4. 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
  5. 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
  6. 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
  7. 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
  8. 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
  9. 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
  10. 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
  11. 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
  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) 12 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) 13 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) 14 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) 15 potatotips #67
  16. 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
  17. 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
  18. 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)
  19. 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)
  20. 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)
  21. 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()))
  22. 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()))
  23. 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()))
  24. 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()))
  25. 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()))
  26. 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()))
  27. 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()))
  28. 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 */ }
  29. 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") } } }
  30. 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) } }
  31. 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) } }
  32. 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)
  33. 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)
  34. 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)
  35. 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
  36. Make the objects serializable with kotlinx.serialization Keishin Yokomaku (@KeithYokoma)
 potatotips

    #67