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
    Keishin Yokomaku (@KeithYokoma)

    potatotips #67

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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)

    View Slide

  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)

    View Slide

  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)

    View Slide

  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()))

    View Slide

  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()))

    View Slide

  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 {
    addSubclass()
    addSubclass()
    }
    }
    val json = Json(JsonConfiguration.Stable, sampleTypeModule)
    // {"d":{"type":"com.sample.FooType", "text":"foo"}}
    val serialized = json.stringify(SampleScreenState(FooType()))

    View Slide

  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()))

    View Slide

  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()))

    View Slide

  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 {
    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()))

    View Slide

  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 { /* 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()))

    View Slide

  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 {
    /* implementation */
    }

    View Slide

  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 {
    override val descriptor =

    object : SerialClassDescImpl(FooType::class.java.name) {
    init {
    // remember this element is the first element (index = 0)
    addElement("text")
    }
    }
    }

    View Slide

  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 {
    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)
    }
    }

    View Slide

  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 {
    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)
    }
    }

    View Slide

  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)

    View Slide

  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)

    View Slide

  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)

    View Slide

  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

    View Slide

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

    potatotips #67

    View Slide