Serialization
Serialization is the process of converting data used by an application to a format that can be transferred over a network or stored in a database or a file. In turn, deserialization is the opposite process of reading data from an external source and converting it into a runtime object. Together they are an essential part of most applications that exchange data with third parties.
Some data serialization formats, such as JSON and protocol buffers are particularly common. Being language-neutral and platform-neutral, they enable data exchange between systems written in any modern language.
In Kotlin, data serialization tools are available in a separate component, kotlinx.serialization. It consists of two main parts: the Gradle plugin – org.jetbrains.kotlin.plugin.serialization and the runtime libraries.
Libraries
kotlinx.serialization provides sets of libraries for all supported platforms – JVM, JavaScript, Native – and for various serialization formats – JSON, CBOR, protocol buffers, and others. You can find the complete list of supported serialization formats below.
All Kotlin serialization libraries belong to the org.jetbrains.kotlinx: group. Their names start with kotlinx-serialization- and have suffixes that reflect the serialization format. Examples:
org.jetbrains.kotlinx:kotlinx-serialization-json provides JSON serialization for Kotlin projects.
org.jetbrains.kotlinx:kotlinx-serialization-cbor provides CBOR serialization.
Platform-specific artifacts are handled automatically; you don’t need to add them manually. Use the same dependencies in JVM, JS, Native, and multiplatform projects.
Note that the kotlinx.serialization libraries use their own versioning structure, which doesn’t match Kotlin’s versioning. Check out the releases on GitHub to find the latest versions.
Formats
kotlinx.serialization includes libraries for various serialization formats:
Note that all libraries except JSON serialization ( kotlinx-serialization-core ) are Experimental, which means their API can be changed without notice.
There are also community-maintained libraries that support more serialization formats, such as YAML or Apache Avro. For detailed information about available serialization formats, see the kotlinx.serialization documentation.
Example: JSON serialization
Let’s take a look at how to serialize Kotlin objects into JSON.
Before starting, you’ll need to configure your build script so that you can use Kotlin serialization tools in your project:
Apply the Kotlin serialization Gradle plugin org.jetbrains.kotlin.plugin.serialization (or kotlin(“plugin.serialization”) in the Kotlin Gradle DSL).
Add the JSON serialization library dependency: org.jetbrains.kotlinx:kotlinx-serialization-json:1.3.1
Now you’re ready to use the serialization API in your code. The API is located in the the kotlinx.serialization package and its format-specific subpackages such as kotlinx.serialization.json .
First, make a class serializable by annotating it with @Serializable .
You can now serialize an instance of this class by calling Json.encodeToString() .
As a result, you get a string containing the state of this object in the JSON format:
You can also serialize object collections, such as lists, in a single call.
To deserialize an object from JSON, use the decodeFromString() function:
For more information about serialization in Kotlin, see the Kotlin Serialization Guide.
Источник
Сериализация Kotlin с помощью Kotlinx.Serialization
После работы над мультиплатформенной библиотекой, которая собирала .framework и .aar артефакты, я пришел к выводу, что есть большое количество полезных вещей, уже есть в Kotlin, но многие из нас никогда о них не знали.
Одна из вещей, о которой вы обязательно должны позаботиться при создании мультиплатформенного проекта — это библиотеки, которые вы используете при разработке. Лучше всего стоит придерживаться решений, предоставленных Kotlin «из коробки».
Так, когда я попал в ситуацию, когда появилась необходимость сериализовать JSON документ, который нужно было использовать на двух платформах(iOS и Android), появились проблемы в компиляции проекта под iOS. После небольших поисков, я нашёл Kotlinx Serializtion library.
Если быть откровенным, я никогда не знал об этой библиотеки, так что эта публикация в большей степени для людей, которые так же как и я не знали об этом инструменте.
Процесс настройки проекта для использования плагина достаточно хорошо описан в репозитории на Гитхабе. Но я буду настраивать проект как для Android, так и для мультиплатформенного использования.
Единственное, что надо сделать при мультиплатформенной компиляции, нужно добавить зависимости в конец зависитмостей в вашем нативном grandle файле.
Пример Grundle для мультиплатформенного использования
Для Android
Сериализация
Чтобы сериализировать класс, просто добавьте перед ним аннотацию @Serializable
Серилиазация работает и с классами данных
Теперь, попробуем написать небольшой пример для преобразования JSON в объект и обратно.
@Transient и @Optional
Еще две аннотации о который стоит рассказать это:
- @Transient — Покажет Serializer’y что поле нужно проигнорировать.
- @Optional — Serializer не остановиться и не выкинет ошибку, если поле отсутствует, но в тоже самое время значение по-умолчанию все равно должно быть установлено.
Пример для Android с использованием Retrofit
Для тех, кто хочет использовать этот плагин в разработке для Андроида, Retrofit 2 имеет адаптер. Ссылка на адаптер.
Если ваш класс уже имеет аннотации, то после отправки запроса ваш класс должен превратиться в JSON объект.
В общем, сериализация в Kotlin’e является отличным дополнением для любого проекта и делает сам процесс сохранения данных в строчке или JSON объекте гораздо более простым менее трудозатратным.
Источник
Android kotlin serialization json
This is the first chapter of the Kotlin Serialization Guide. This chapter shows the basic use of Kotlin Serialization and explains its core concepts.
Table of contents
To convert an object tree to a string or to a sequence of bytes, it must come through two mutually intertwined processes. In the first step, an object is serialized—it is converted into a serial sequence of its constituting primitive values. This process is common for all data formats and its result depends on the object being serialized. A serializer controls this process. The second step is called encoding—it is the conversion of the corresponding sequence of primitives into the output format representation. An encoder controls this process. Whenever the distinction is not important, both the terms of encoding and serialization are used interchangeably.
The reverse process starts with parsing of the input format and decoding of primitive values, followed by deserialization of the resulting stream into objects. We’ll see details of this process later.
For now, we start with JSON encoding.
The whole process of converting data into a specific format is called encoding. For JSON we encode data using the Json.encodeToString extension function. It serializes the object that is passed as its parameter under the hood and encodes it to a JSON string.
Let’s start with a class describing a project and try to get its JSON representation.
When we run this code we get the exception.
Serializable classes have to be explicitly marked. Kotlin Serialization does not use reflection, so you cannot accidentally deserialize a class which was not supposed to be serializable. We fix it by adding the @Serializable annotation.
The @Serializable annotation instructs the Kotlin Serialization plugin to automatically generate and hook up a serializer for this class. Now the output of the example is the corresponding JSON.
There is a whole chapter about the Serializers. For now, it is enough to know that they are automatically generated by the Kotlin Serialization plugin.
The reverse process is called decoding. To decode a JSON string into an object, we’ll use the Json.decodeFromString extension function. To specify which type we want to get as a result, we provide a type parameter to this function.
As we’ll see later, serialization works with different kinds of classes. Here we are marking our Project class as a data class , not because it is required, but because we want to print its contents to verify how it decodes.
Running this code we get back the object.
This section goes into more details on how different @Serializable classes are handled.
Backing fields are serialized
Only a class’s properties with backing fields are serialized, so properties with a getter/setter that don’t have a backing field and delegated properties are not serialized, as the following example shows.
We can clearly see that only the name and stars properties are present in the JSON output.
Constructor properties requirement
If we want to define the Project class so that it takes a path string, and then deconstructs it into the corresponding properties, we might be tempted to write something like the code below.
This class does not compile because the @Serializable annotation requires that all parameters of the class’s primary constructor be properties. A simple workaround is to define a private primary constructor with the class’s properties, and turn the constructor we wanted into the secondary one.
Serialization works with a private primary constructor, and still serializes only backing fields.
This example produces the expected output.
Another case where you might want to introduce a primary constructor parameter without a property is when you want to validate its value before storing it to a property. To make it serializable you shall replace it with a property in the primary constructor, and move the validation to an init < . >block.
A deserialization process works like a regular constructor in Kotlin and calls all init blocks, ensuring that you cannot get an invalid class as a result of deserialization. Let’s try it.
Running this code produces the exception:
An object can be deserialized only when all its properties are present in the input. For example, run the following code.
It produces the exception:
This problem can be fixed by adding a default value to the property, which automatically makes it optional for serialization.
It produces the following output with the default value for the language property.
Optional property initializer call
When an optional property is present in the input, the corresponding initializer for this property is not even called. This is a feature designed for performance, so be careful not to rely on side effects in initializers. Consider the example below.
Since the language property was specified in the input, we don’t see the «Computing» string printed in the output.
A property with a default value can be required in a serial format with the @Required annotation. Let us change the previous example by marking the language property as @Required .
We get the following exception.
A property can be excluded from serialization by marking it with the @Transient annotation (don’t confuse it with kotlin.jvm.Transient). Transient properties must have a default value.
Attempts to explicitly specify its value in the serial format, even if the specified value is equal to the default one, produces the following exception.
The ‘ignoreUnknownKeys’ feature is explained in the Ignoring Unknown Keys section section.
Defaults are not encoded
Default values are not encoded by default in JSON. This behavior is motivated by the fact that in most real-life scenarios such configuration reduces visual clutter, and saves the amount of data being serialized.
It produces the following output, which does not have the language property because its value is equal to the default one.
See Encoding defaults section on how this behavior can be configured for JSON.
Nullable properties are natively supported by Kotlin Serialization.
This example does not encode null in JSON because Defaults are not encoded.
Type safety is enforced
Kotlin Serialization strongly enforces the type safety of the Kotlin programming language. In particular, let us try to decode a null value from a JSON object into a non-nullable Kotlin property language .
Even though the language property has a default value, it is still an error to attempt to assign the null value to it.
It might be desired, when decoding 3rd-party JSONs, to coerce null to a default value. The corresponding feature is explained in the Coercing input values section.
Serializable classes can reference other classes in their serializable properties. The referenced classes must be also marked as @Serializable .
When encoded to JSON it results in a nested JSON object.
References to non-serializable classes can be marked as Transient properties, or a custom serializer can be provided for them as shown in the Serializers chapter.
No compression of repeated references
Kotlin Serialization is designed for encoding and decoding of plain data. It does not support reconstruction of arbitrary object graphs with repeated object references. For example, let us try to serialize an object that references the same owner instance twice.
We simply get the owner value encoded twice.
Attempt to serialize a circular structure will result in stack overflow. You can use the Transient properties to exclude some references from serialization.
Generic classes in Kotlin provide type-polymorphic behavior, which is enforced by Kotlin Serialization at compile-time. For example, consider a generic serializable class Box .
The Box class can be used with builtin types like Int , as well as with user-defined types like Project .
The actual type that we get in JSON depends on the actual compile-time type parameter that was specified for Box .
If the actual generic type is not serializable a compile-time error will be produced.
Serial field names
The names of the properties used in encoded representation, JSON in our examples, are the same as their names in the source code by default. The name that is used for serialization is called a serial name, and can be changed using the @SerialName annotation. For example, we can have a language property in the source with an abbreviated serial name.
Now we see that an abbreviated name lang is used in the JSON output.
Источник