Kotlin-Updates: kotlinx.serialization 1.2

In Teil 3 meiner Kotlin-Updates-Serie fasse ich ganz kurz die Neuerungen in der Bibliothek kotlinx.serialization zusammen, die seit dem Erscheinen meines Kotlin-Buchs erfolgt sind.

Dieser Text bezieht sich auf die folgenden Versionsnummern:

IntelliJ: 2021.1
Kotlin: 1.5.20
kotlinx.serialization: 1.2.1
Gradle: 6.8

Gradle

Der Umstieg von kotlinx.serialization 1.0 auf 1.2 gelingt weitgehend problemlos, sobald einmal build.gradle passt. Zuerst sollten Sie Gradle selbst auf eine aktuelle Version bringen (Datei graddle/wrapper/gradle-wrapper.properties). Ich habe das Testprojekt aus dem Buch (Download-Link am Ende des Artikels) auf Version 6.8 aktualisiert. IntelliJ verwendet nach der Änderung in gradle-wrapper.properties mitunter weiter die alte Gradle-Version und zeigt merkwürdige Fehler an. Abhilfe: Projekt schließen und neu laden.

In build.gradle müssen Sie nicht nur auf die richtigen Versionen achten, sondern auch, dass es nun getrennte Bibliotheken für unterschiedliche Serialisierungsformate gibt, die extra aufgezählt werden müssen. Wo bisher implementation "...-core:n.n" reichte, ist jetzt zumindest eine weitere Zeile mit implementation "...-json:n.n" erforderlich. Das jcenter-Repository ist zu entfernen, Gradle findet alle erforderlichen Bibliotheken in mavenCentral.

Ein minimales Muster mit aktuellen Versionsnummern sieht so aus:

// Datei build.gradle
plugins {
    id 'java'
    id 'org.jetbrains.kotlin.jvm' version '1.5.20'
    id 'org.jetbrains.kotlin.plugin.serialization' version "1.5.20"
}
repositories {
    mavenCentral()
}
dependencies {
    implementation "org.jetbrains.kotlinx:kotlinx-serialization-core:1.2.1"
    // die folgende Zeile muss bei vorhandenen Projekten hinzugefügt werden!
    implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.2.1"
}
compileKotlin {
    kotlinOptions.jvmTarget = "11"
}

Neu in Version 1.2

kotlinx.serialization ist laut JetBrains bis zu doppelt so schnell als die bisherige Implementierung. Außerdem werden diverse neue Kotlin-Dateitypen, u.a. UInt oder UByte, jetzt nativ unterstützt. Bei meinen Tests habe ich keine Inkompatibilitäten zwischen Version 1.0 und 1.2 festgestellt.

Zur schon bekannten Annotation @SerialName, mit der Sie den Zusammenhang zwischen der Variable/Eigenschaft x in Ihrem Code und dem Serialisierungsfeld y herstellen konnte, gibt es nun neu auch die Annotation @JsonNames, um mehrere alternative Namen in der JSON-Datei einer Variablen/Eigenschaft in Ihrem Code zuzuordnen:

// schon bisher möglich
@Serializable
data class Class1(@JsonNames("title") val name: String)
// der JSON-Namen title ist der Eigenschaft name 
// der Klasse Class1 zugeordnet


// neu
@Serializable
data class Class2(@JsonNames("title") val name: String)
// die JSON-Namen title UND name sind gleichwertig und werden 
// beide der Eigenschaft name der Klasse Class2 zugeordnet

Schließlich wurde die Dokumentation komplett überarbeitet (siehe die Links am Ende des Artikels).

Serialisierung von LocalDateTime

Die Serialisierung von Java-Datentypen LocalDateTime bzw. von eigenen Datentypen funktioniert nur mit Ihrer Mithilfe. Die Vorgehensweise ist weiterhin experimentell. Sie hat sich in winzigen Details geändert. Zuerst müssen Sie eine Klasse mit den (De)serialisierungs-Funktionen programmieren, die die Schnittstelle KSerialize<typ> implementiert. Der Datentyp von deserialize muss jetzt explizit angegeben werden, damit für Kotlin klar ist, ob das Ergebnis auch null sein kann.

@ExperimentalSerializationApi
@Serializer(LocalDateTime::class)
object LocalDateTimeSerializer : KSerializer<LocalDateTime> {

    override fun serialize(encoder: Encoder, value: LocalDateTime) =
        encoder.encodeString(value.format(DateTimeFormatter.ISO_DATE_TIME))

    // neu: Ergebnistyp muss explizit angegeben werden
    override fun deserialize(decoder: Decoder) : LocalDateTime =
        LocalDateTime.parse(decoder.decodeString(),
            DateTimeFormatter.ISO_DATE_TIME)
}

Damit ist es aber noch nicht getan: Sie müssen Kotlin explizit darauf hinweisen, dass Ihr Serialisierer für einen bestimmten Datentyp verwendet werden soll. Dazu bestehen verschiedene Möglichkeiten. Die in den meisten Fällen einfachste Variante besteht darin, dass Sie am Beginn der Kotlin-Datei mit der Annotation @file:UseSerializers die gewünschte (De-)Serialisierungsklasse spezifizieren. Diese Einstellung gilt für die gesamte Datei und ist insbesondere dann zweckmäßig, wenn der betreffende Datentyp in vielen Klassen vorkommt.

// am Beginn der Code-Datei angeben
@file:UseSerializers(LocalDateTimeSerializer::class)

IntelliJ zeigt nun eine Warnung an, dass UseSerializers experimentell ist und mit der Annotation @ExperimentalSerializationApi gekennzeichnet werden sollte. Dazu habe ich aber keinen Weg gefunden. Sobald ich die Annotation vorangestellt habe, betrachtet der Compiler die gesamte Anweisung als Syntaxfehler.

Quellen/Links

Download

Das aktualisierte Beispielprojekt zu Abschnitt 18.4 können Sie hier herunterladen:

https://kofler.info/uploads/kotlin/kap18-json.zip

Die Kotlin-Updates-Serie

Weitere Kotlin-Update-Artikel finden Sie hier auf meiner Website:

https://kofler.info/tag/kotlin-updates