Сериализация Kotlin с помощью Kotlinx.Serialization



    После работы над мультиплатформенной библиотекой, которая собирала .framework и .aar артефакты, я пришел к выводу, что есть большое количество полезных вещей, уже есть в Kotlin, но многие из нас никогда о них не знали.

    Одна из вещей, о которой вы обязательно должны позаботиться при создании мультиплатформенного проекта — это библиотеки, которые вы используете при разработке. Лучше всего стоит придерживаться решений, предоставленных Kotlin «из коробки».
    Так, когда я попал в ситуацию, когда появилась необходимость сериализовать JSON документ, который нужно было использовать на двух платформах(iOS и Android), появились проблемы в компиляции проекта под iOS. После небольших поисков, я нашёл Kotlinx Serializtion library.
    Если быть откровенным, я никогда не знал об этой библиотеки, так что эта публикация в большей степени для людей, которые так же как и я не знали об этом инструменте.

    Процесс настройки проекта для использования плагина достаточно хорошо описан в репозитории на Гитхабе. Но я буду настраивать проект как для Android, так и для мультиплатформенного использования.

    Единственное, что надо сделать при мультиплатформенной компиляции, нужно добавить зависимости в конец зависитмостей в вашем нативном Grundle файле.

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

    Пример Grundle для мультиплатформенного использования


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

    Для 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'
    }

    Сериализация


    Чтобы сериализировать класс, просто добавьте перед ним аннотацию @Serializable

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

    Серилиазация работает и с классами данных

    Теперь, попробуем написать небольшой пример для преобразования JSON в объект и обратно.

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

    @Transient и @Optional


    Еще две аннотации о который стоит рассказать это:

    • @Transient — Покажет Serializer'y что поле нужно проигнорировать.
    • @Optional — Serializer не остановиться и не выкинет ошибку, если поле отсутствует, но в тоже самое время значение по-умолчанию все равно должно быть установлено.

    @Optional
    var isOptional: Boolean = false
    
    @Transient
    var isTransient: Boolean = false
    

    Пример для Android с использованием Retrofit


    Для тех, кто хочет использовать этот плагин в разработке для Андроида, Retrofit 2 имеет адаптер. Ссылка на адаптер.

    Чуть-чуть кода:

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

    Если ваш класс уже имеет аннотации, то после отправки запроса ваш класс должен превратиться в JSON объект.

    В общем, сериализация в Kotlin'e является отличным дополнением для любого проекта и делает сам процесс сохранения данных в строчке или JSON объекте гораздо более простым менее трудозатратным.

    Список репозиториев


    1. KotlinxRetrofit — небольшой работащий пример использования сериализации на Android
    2. kotlinx.serialization — Основной репозиторий библиотеки

    JakeWharton/retrofit2-kotlinx-serialization-converter — адаптер для Retrofit
    • –8
    • 2,6k
    • 3
    Поделиться публикацией

    Комментарии 3

      +2
      Кажется, ссылка на оригинал потерялась.
        +1
        Перевод ужасный.
          0

          Как можно было при переводе превратить Gradle в Grundle?

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое