MWCodebymw.de ↗
Kotlin

kotlinx.serialization: JSON und data class ohne Handarbeit verbinden

Eine API antwortet mit JSON, deine App will eine data class. Dazwischen steht in vielen Projekten ein handgeschriebener Parser. Mit kotlinx.serialization sind es eine Annotation und eine Zeile – und ein Schalter, ohne den deine App beim nächsten API-Update abstürzt.

Jede App, die ich baue, redet irgendwann mit einem Server, und der antwortet mit JSON. Früher stand dafür bei mir eine Klasse voller getString("name")-Aufrufe – Zeile für Zeile von Hand, und bei jedem neuen Feld musste ich sie anfassen. kotlinx.serialization nimmt einem das ab: Du beschreibst die Form einmal als data class, den Rest erzeugt der Compiler.

Einrichten

Im Modul-build.gradle.kts:

plugins {
    kotlin("plugin.serialization") version "2.1.0"
}
dependencies {
    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")
}

Die data class beschreibt die Antwort

import kotlinx.serialization.*
import kotlinx.serialization.json.*

@Serializable
data class Ort(
    val name: String,
    val land: String = "DE",                    // fehlt das Feld, greift der Standard
    @SerialName("lat") val breite: Double,      // anderer Name im JSON als im Code
    @SerialName("lon") val laenge: Double,
    val einwohner: Int? = null                  // darf auch null sein
)

val json = Json { ignoreUnknownKeys = true }

val antwort = """{"name":"Regensburg","lat":49.0134,"lon":12.1016,"zeitzone":"Europe/Berlin"}"""
val ort: Ort = json.decodeFromString(antwort)

println(ort.name)        // Regensburg
println(ort.land)        // DE  (stand nicht im JSON – Standardwert)
println(ort.einwohner)   // null

val zurueck: String = json.encodeToString(ort)

Kein Parser, keine Reflexion zur Laufzeit: Der Compiler erzeugt beim Bauen einen Serializer für jede @Serializable-Klasse. Deshalb bekommst du Fehler bereits beim Kompilieren und nicht erst, wenn der Nutzer die Seite öffnet.

Der Schalter, ohne den es knallt

ignoreUnknownKeys = true ist keine Kosmetik. Ohne ihn wirft decodeFromString eine Ausnahme, sobald das JSON ein Feld enthält, das deine Klasse nicht kennt. Und genau das passiert früher oder später: Der Serverbetreiber ergänzt ein Feld, seine App-Version ändert sich nicht – und deine stürzt ab. Im Beispiel oben ist "zeitzone" so ein Feld.

Zwei weitere Einstellungen, die ich fast immer setze:

val json = Json {
    ignoreUnknownKeys = true   // unbekannte Felder überspringen statt abstürzen
    coerceInputValues = true   // null in einem nicht-nullbaren Feld → Standardwert
    prettyPrint = false        // beim Senden: kompakt, spart Bytes
}

coerceInputValues ist der zweite Rettungsring: Manche APIs schicken null, wo laut Dokumentation ein Wert stehen sollte. Ohne den Schalter fliegt eine Ausnahme, mit ihm greift der Standardwert deiner data class.

Was du dir merken solltest

Ein Feld ohne Standardwert ist Pflicht. Fehlt es im JSON, gibt es eine MissingFieldException – das ist gewollt, denn ein Pflichtfeld, das schweigend null wird, ist die schlimmere Variante. Wenn ein Feld optional ist, gib ihm einen Standardwert oder mach es nullbar.

Standardwerte werden beim Senden nicht mitgeschrieben. encodeToString lässt Felder weg, die noch auf ihrem Standard stehen. Brauchst du sie in der ausgehenden Nachricht, setz encodeDefaults = true.

Für Retrofit gibt es einen passenden Converter (Json.asConverterFactory), sodass du decodeFromString gar nicht selbst aufrufen musst – die Schnittstelle liefert direkt deine data class.

Für die Android-Apps, die ich baue, ist das inzwischen der Standardweg: eine Datei mit @Serializable-Klassen, ein Json-Objekt mit den drei Schaltern oben, fertig. Wenn du gerade eine App planst und über solche Grundentscheidungen sprechen willst – meld dich einfach über bymw.de, ich schaue mir das gern mit dir an.

Quellen

#kotlinx.serialization#JSON#Android#Kotlin#Retrofit

Du brauchst mehr als ein Snippet?

Ich entwickle Android-Apps in Kotlin und moderne Websites für Selbstständige und kleine Unternehmen — von der ersten Idee bis zum Release.

Projekt anfragen →