Serialization of Kotlin using Kotlinx.Serialization



    After working on a multiplatform library that collected .framework and .aar artifacts, I came to the conclusion that there are a lot of useful things already in Kotlin, but many of us never knew about them.

    One of the things that you must take care when creating a multiplatform project is the libraries that you use when developing. It is best to adhere to the solutions provided by Kotlin out of the box.
    So, when I got into a situation when it became necessary to serialize a JSON document that needed to be used on two platforms (iOS and Android), there were problems in compiling an iOS project. After a bit of searching, I found the Kotlinx Serializtion library .
    To be frank, I never knew about this library, so this publication is more for people who, like me, did not know about this tool.

    The process of setting up a project for using a plug-in is quite well described inrepositories on Github . But I will customize the project both for Android and for multi-platform use.

    The only thing that needs to be done in multiplatform compilation is to add dependencies to the end of dependencies in your native Grundle file.

    implementation org.jetbrains.kotlinx:kotlinx-serialization-runtime-native:0.9.1

    Grundle example for multiplatform use


    plugins {
        id 'kotlin-multiplatform' version '1.3.11'
        id 'kotlinx-serialization' version '1.3.10'
    }
    repositories {
        google()
        jcenter()
        mavenCentral()
        maven { url "https://kotlin.bintray.com/kotlinx" }
    }
    apply plugin: 'com.android.library'
    apply plugin: 'kotlin-android-extensions'
    android {
        compileSdkVersion 28
        defaultConfig {
            minSdkVersion 15
            targetSdkVersion 28
            versionCode 1
            versionName "1.0"
            testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        }
        buildTypes {
            release {
                minifyEnabled false
            }
        }
    }
    dependencies {
        implementation fileTree(dir: 'libs', include: ['*.jar'])
        implementation 'com.android.support:appcompat-v7:28.0.0'
        implementation 'com.android.support.constraint:constraint-layout:1.1.3'
        androidTestImplementation 'com.android.support.test:runner:1.0.2'
    }
    kotlin {
        targets {
            fromPreset(presets.android, 'android')
            // Пресет для эмулятора iPhone// Поменяйте гп presets.iosArm64 (или iosArm32) чтобы собрать библиотеку для iPhone
            fromPreset(presets.iosX64, 'ios') {
                compilations.main.outputKinds('FRAMEWORK')
            }
        }
        sourceSets {
            commonMain {
                dependencies {
                    implementation 'org.jetbrains.kotlin:kotlin-stdlib-common'
                    implementation 'org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.9.1'
                }
            }
            commonTest {
                dependencies {
            		implementation 'org.jetbrains.kotlin:kotlin-test-common'
            		implementation 'org.jetbrains.kotlin:kotlin-test-annotations-common'
                }
            }
            androidMain {
                dependencies {
                    implementation 'org.jetbrains.kotlin:kotlin-stdlib'
                }
            }
            androidTest {
                dependencies {
                }
            }
            iosMain {
                dependencies{
                    implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime-native:0.9.1"
                }
            }
            iosTest {
            }
        }
    }

    For Android


    apply plugin: 'com.android.application'
    apply plugin: 'kotlin-android'
    apply plugin: 'kotlin-android-extensions'
    apply plugin: 'kotlinx-serialization'
    android {
        compileSdkVersion 28
        defaultConfig {
            applicationId "com.example.smile.kotlinxretrosample"
            minSdkVersion 16
            targetSdkVersion 28
            versionCode 1
            versionName "1.0"
            testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
        }
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            }
        }
    }
    dependencies {
        implementation fileTree(dir: 'libs', include: ['*.jar'])
        implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
        implementation "org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.9.1"
        implementation 'com.android.support:appcompat-v7:28.0.0'
        implementation 'com.android.support.constraint:constraint-layout:1.1.3'
        implementation 'com.android.support:design:28.0.0'
        implementation 'com.squareup.retrofit2:retrofit:2.5.0'
        implementation 'com.squareup.okhttp3:okhttp:3.12.0'
        testImplementation 'junit:junit:4.12'
        androidTestImplementation 'com.android.support.test:runner:1.0.2'
        androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    }

    Serialization


    To serialize a class, simply add an annotation before it. @Serializable

    import kotlinx.serialization.Serializable
    @SerializableclassField{
        var length: Int = 0var hint: String = ""var required: Boolean = false
    }
    

    Serialization works with data classes

    Now, let's write a small example to convert JSON to an object and back.

    /*
    * {
        length = 20
        hint = "example"
        required= false
    }
    */funtoObject(stringValue: String): Field {
            return JSON.parse(Field.serializer(), stringValue)
        }
        funtoJson(field: Field): String {
            // Обратите внимание, что мы вызываем Serializer, который автоматически сгенерирован из нашего класса// Сразу после того, как мы добавили аннотацию @Serializerreturn JSON.stringify(Field.serializer(), field)
        }

    @Transient and @Optional


    Two more annotations about which are worth telling this:

    • @Transient - Shows Serializer that the field needs to be ignored.
    • @Optional - Serializer will not stop and will not throw an error if the field is absent, but at the same time the default value should still be set.

    @Optionalvar isOptional: Boolean = false@Transientvar isTransient: Boolean = false

    An example for Android using Retrofit


    For those who want to use this plugin in the development for Android, Retrofit 2 has an adapter. Link to the adapter .

    A bit of code:

    un createRetrofit(): Retrofit {
            val contentType = MediaType.get("application/json")
            return Retrofit.Builder()
                .addConverterFactory(serializationConverterFactory(contentType, JSON))
                .baseUrl(BASE_URL)
                .client(provideOkhttpClient())
                .build()
        }

    If your class already has annotations, then after sending the request your class should turn into a JSON object.

    In general, serialization in Kotlin is an excellent addition to any project and makes the process of storing data in a string or a JSON object much simpler and less labor-intensive.

    List of repositories


    1. KotlinxRetrofit - a small working example of using serialization on Android
    2. kotlinx.serialization - The main library repository

    JakeWharton / retrofit2-kotlinx serialization-converter - adapter for Retrofit

    Also popular now: