27 January 2019

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

Development for AndroidKotlin


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

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

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

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

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
Tags:kotlinjsonserializationandroidiosjetbrains
Hubs: Development for Android Kotlin
-8
13.7k 25
Comments 3
Popular right now
Top of the last 24 hours