kotlinx.serialization — multiplatformní knihovna od JetBrains pro převod Kotlin objektů do JSON, ProtoBuf, CBOR a dalších formátů bez použití reflexe. Na rozdíl od Gson a Moshi generuje kód serializátoru v fázi kompilace prostřednictvím anotace @Serializable, což poskytuje vysoký výkon a typovou bezpečnost. Podle GitHub Kotlin/kotlinx.serialization knihovna podporuje Kotlin/JVM, Kotlin/Native, Kotlin/JS a Kotlin/Wasm.
Hlavní body
kotlinx.serialization — je vestavěná serializační knihovna pro Kotlin, vyvinutá společností JetBrains jako součást oficiálního ekosystému Kotlin. Jejím hlavním rozdílem od řešení třetích stran (Gson, Moshi, Jackson) je, že nepoužívá reflexi za běhu. Místo toho je kód serializátoru generován ve fázi kompilace pomocí Kotlin Symbol Processing (KSP) nebo Kotlin Compiler Plugin. To poskytuje zvýšení výkonu až 3-5krát ve srovnání s Gson a zaručuje typovou bezpečnost.
Knihovna oficiálně podporuje čtyři formáty: JSON (prostřednictvím modulu kotlinx-serialization-json), ProtoBuf (kotlinx-serialization-protobuf), CBOR (kotlinx-serialization-cbor) a HOCON (kotlinx-serialization-hocon). Formáty se přidávají jako samostatné závislosti v build.gradle.kts, což umožňuje nezatahovat do projektu zbytečné knihovny. Pro každý formát existuje vlastní sada konfiguračních parametrů.
Multiplatformnost — klíčová vlastnost knihovny. Stejná třída s @Serializable pracuje na všech cílových platformách: JVM (Android, Backend), Native (iOS), JS (Web, React) a Wasm (WebAssembly). Vývojář nemusí psát různé implementace serializace pro každou platformu — kód zůstává jednotný. To je zvláště cenné v projektech Kotlin Multiplatform Mobile (KMM), kde je sdílený kód rozdělen mezi Android a iOS.
Generování kódu v kotlinx.serialization probíhá ve třech fázích. V první fázi kompilátor Kotlin detekuje anotaci @Serializable na třídě a předá ji pluginu Kotlin Symbol Processing (KSP). Ve druhé fázi KSP generuje objekt serializátoru, který implementuje rozhraní KSerializer. Ve třetí fázi je generovaný kód zkompilován spolu se zdrojovým kódem projektu. Výsledkem je, že žádná z těchto fází není provedena za běhu aplikace.
Generovaný serializátor pracuje přímo s poli třídy prostřednictvím jejich getterů a setterů, bez reflexe. To znamená, že pole s modifikátorem private jsou také serializována, pokud jsou označena @Serializable. Výkon tohoto přístupu se blíží ruční serializaci: pro jednoduché třídy (5-10 polí) je doba serializace 10-50 mikrosekund, pro složité grafy objektů — až 200 mikrosekund na 1000 objektů.
Pro připojení knihovny v projektu Android nebo Kotlin/JVM je třeba přidat plugin a závislosti v build.gradle.kts. Plugin org.jetbrains.kotlin.plugin.serialization ve verzi odpovídající verzi Kotlin aktivuje generování kódu. Knihovna kotlinx-serialization-json se přidává v sekci dependencies s verzí nezávislou na verzi Kotlin.
// build.gradle.kts — připojení kotlinx.serialization
plugins {
val kotlinVersion = "2.1.0"
kotlin("jvm") version kotlinVersion
kotlin("plugin.serialization") version kotlinVersion
}
dependencies {
// Hlavní modul serializace
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")
// Další formáty
implementation("org.jetbrains.kotlinx:kotlinx-serialization-protobuf:1.7.3")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-cbor:1.7.3")
}
JSON — nejoblíbenější formát v kotlinx.serialization. Pro serializaci objektu stačí umístit anotaci @Serializable na data class a zavolat Json.encodeToString(). Pro deserializaci — Json.decodeFromString() s uvedením typu. Knihovna automaticky zpracovává null pole, seznamy, vnořené objekty a enumy. Všechna pole třídy jsou ve výchozím nastavení povinná, pokud není uvedeno jinak.
Konfigurace JSON se provádí prostřednictvím Json {} builder. Do konstruktoru lze předat ignoreUnknownKeys = true pro přeskočení neznámých polí při deserializaci, prettyPrint = true pro formátovaný výstup, coerceInputValues = true pro převod nesprávných hodnot na výchozí hodnoty. K dispozici jsou také nastavení encodeDefaults (serializace polí s výchozími hodnotami) a classDiscriminator (název pole pro polymorfní serializaci).
// Příklad serializace a deserializace JSON
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonConfiguration
@Serializable
data class Project(
val name: String,
val stars: Int,
val isActive: Boolean = true,
val languages: List<String> = emptyList()
)
fun main() {
val project = Project(
name = "kotlinx.serialization",
stars = 7200,
languages = listOf("Kotlin", "Java")
)
// Serializace do JSON s prettyPrint
val json = Json { prettyPrint = true }
val jsonString = json.encodeToString(project)
println(jsonString)
/*
{
"name": "kotlinx.serialization",
"stars": 7200,
"isActive": true,
"languages": ["Kotlin", "Java"]
}
*/
// Deserializace z JSON
val decoded = json.decodeFromString<Project>(jsonString)
println(decoded.name) // kotlinx.serialization
}
Příklad ukazuje základní cyklus serializace a deserializace. Data class Project s anotací @Serializable automaticky získá encodeToString a decodeFromString. Pole isActive má výchozí hodnotu true — pokud toto pole v JSON chybí, použije se výchozí hodnota. Pokud do JSON přijdou neznámá pole bez ignoreUnknownKeys = true, bude vyvolána výjimka SerializationException.
Sealed class — jeden z nejvýkonnějších případů použití kotlinx.serialization. Knihovna podporuje polymorfní serializaci pro hierarchie sealed class bez dodatečné konfigurace: stačí označit sealed class a všechny její potomky anotací @Serializable. Při serializaci se přidá pole „type” (konfigurovatelné přes classDiscriminator), na jehož základě se při deserializaci určuje konkrétní typ.
// Polymorfní serializace sealed class
@Serializable
sealed class Response
@Serializable
data class Success(val data: String) : Response()
@Serializable
data class Error(val code: Int, val message: String) : Response()
fun main() {
val json = Json { classDiscriminator = "result_type" }
val responses: List<Response> = listOf(
Success(data = "Data loaded"),
Error(code = 404, message = "Not found")
)
val jsonString = json.encodeToString(responses)
println(jsonString)
/*
[
{"result_type":"Success","data":"Data loaded"},
{"result_type":"Error","code":404,"message":"Not found"}
]
*/
val decoded = json.decodeFromString<List<Response>>(jsonString)
when (val first = decoded[0]) {
is Success -> println("Success: ${first.data}")
is Error -> println("Error: ${first.code}")
}
}
Polymorfní serializace sealed class je zvláště užitečná v API klientech, kde server vrací různé typy odpovědí. Bez kotlinx.serialization byste museli napsat ruční deserializátor s when podle diskriminačního pole. S knihovnou se to dělá jednou anotací. classDiscriminator umožňuje přejmenovat pole markeru (výchozí „type”) na libovolnou hodnotu očekávanou serverem.
Knihovna poskytuje sadu anotací pro jemné ladění serializace. Hlavní je @Serializable pro třídu. Další anotace: @SerialName pro určení názvu pole v JSON (pokud se liší od názvu Kotlin), @Transient pro vyloučení pole ze serializace, @Required pro pole, které musí být v JSON přítomno, @EncodeDefault pro vynucenou serializaci pole s výchozí hodnotou.
| Anotace | Účel | Příklad |
|---|---|---|
| @Serializable | Zapíná generování serializátoru pro třídu | @Serializable data class User |
| @SerialName | Určuje alternativní název pole ve formátu | @SerialName(„user_name”) val name: String |
| @Transient | Vylučuje pole ze serializace | @Transient val cache: MutableMap |
| @Required | Pole je povinné v JSON při deserializaci | @Required val id: String |
| @EncodeDefault | Serializuje pole i s výchozí hodnotou | @EncodeDefault val type: Type = Type.A |
| @Serializer | Připojuje vlastní serializátor ke třídě | @Serializer(forClass = Date::class) |
Anotace @SerialName je kritická při práci s API, kde jsou názvy polí v snake_case a styl Kotlin je camelCase. Například server posílá „user_id” a v kódu Kotlin se používá userId. @SerialName(„user_id”) řeší tento problém bez dalších mapperů. @Transient je užitečná pro pole, která není třeba odesílat na server — například dočasné vypočítané hodnoty nebo mezipaměť.
Ve výchozím nastavení jsou všechna pole v kotlinx.serialization povinná. Pokud pole může v JSON chybět, musíte jej učinit nullable (String?) nebo nastavit výchozí hodnotu (val name: String = „”). Existují však situace, kdy pole není nullable v Kotlin, ale může v JSON chybět kvůli verzování API. V tomto případě @SerializationException při absenci pole, zatímco výchozí hodnota vyplní default bez chyby.
KSerializer — rozhraní, které implementují všechny serializátory v kotlinx.serialization. Pokud standardní generování kódu není vhodné (například pro práci s Date, Bitmap nebo specifickým binárním formátem), můžete napsat vlastní serializátor. K tomu je třeba implementovat metody serialize() a deserialize() a poskytnout deskriptor — popis struktury pro schéma formátu.
Vlastní serializátory se připojují dvěma způsoby: prostřednictvím anotace @Serializable(with = MySerializer::class) pro připojení ke konkrétní třídě nebo globálně přes Json { serializersModule = ... } pro připojení ke všem instancím typu. Druhý způsob je preferován pro vestavěné typy (Date, UUID), aby se nemusela psát anotace na každém poli.
// Vlastní serializátor pro java.util.Date
import kotlinx.serialization.KSerializer
import kotlinx.serialization.descriptors.PrimitiveKind
import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
object DateSerializer : KSerializer<Date> {
private val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US)
override val descriptor: SerialDescriptor =
PrimitiveSerialDescriptor("Date", PrimitiveKind.STRING)
override fun serialize(encoder: Encoder, value: Date) {
encoder.encodeString(dateFormat.format(value))
}
override fun deserialize(decoder: Decoder): Date {
return dateFormat.parse(decoder.decodeString())
}
}
// Použití vlastního serializátoru
@Serializable
data class Event(
val title: String,
@Serializable(with = DateSerializer::class)
val date: Date
)
fun main() {
val json = Json { prettyPrint = true }
val event = Event("Vydání", Date())
println(json.encodeToString(event))
}
V příkladu DateSerializer převádí java.util.Date na řetězec ISO 8601. Bez vlastního serializátoru kotlinx.serialization neumí pracovat s Date — to je typ, který není součástí standardní knihovny Kotlin. @Serializable(with = DateSerializer::class) na konkrétním poli připojí serializátor pouze pro toto pole. Pro globální registraci všech Date použijte Json { serializersModule = SerializersModule { contextual(DateSerializer) } }.
kotlinx.serialization se neomezuje pouze na JSON. Knihovna podporuje čtyři vestavěné formáty, každý s vlastním modulem a konfigurací. JSON (kotlinx-serialization-json) — univerzální, čitelný pro člověka, vhodný pro REST API. ProtoBuf (kotlinx-serialization-protobuf) — binární, kompaktní, s povinným schématem, pro vysoce zatížené mikroslužby. CBOR (kotlinx-serialization-cbor) — binární obdoba JSON, vhodná pro IoT a mobilní zařízení s omezeným provozem. HOCON (kotlinx-serialization-hocon) — konfigurační formát, kompatibilní s TypeSafe Config.
| Formát | Modul | Typ | Schéma | Typické použití |
|---|---|---|---|---|
| JSON | kotlinx-serialization-json | Textový | Volitelné | REST API, ukládání dat |
| ProtoBuf | kotlinx-serialization-protobuf | Binární | Povinné (.proto) | Mikroslužby, gRPC |
| CBOR | kotlinx-serialization-cbor | Binární | Volitelné | IoT, mobilní zařízení |
| HOCON | kotlinx-serialization-hocon | Textový | Volitelné | Konfigurační soubory |
ProtoBuf vyžaduje definici schématu v .proto souborech, ale kotlinx-serialization-protobuf generuje Kotlin třídy přímo z @Serializable bez .proto. To zjednodušuje vývoj: stačí anotovat data class a použít ProtoBuf.encodeToByteArray(). CBOR je zvláště relevantní pro framework Android, když je třeba přenášet kompaktní binární data přes NFC nebo BLE. Velikost CBOR zprávy je v průměru o 20-30% menší než JSON při stejném souboru dat.
Pro REST API v mobilní aplikaci je optimální JSON — ladí se bez dalších nástrojů, je čitelný v logách a kompatibilní s jakýmkoli backendem. Pokud aplikace přenáší velké objemy dat mezi mikroslužbami (stovky megabajtů) — ProtoBuf poskytne zvýšení rychlosti až 5krát díky binárnímu kódování. Pro ukládání nastavení v souborech použijte HOCON nebo JSON. Pro zařízení s přísnými omezeními provozu (IoT senzory) — CBOR.
První chyba — ignorování neznámých klíčů při deserializaci. Pokud server přidal nové pole a máte ignoreUnknownKeys = false, aplikace spadne s SerializationException. Ve výchozím nastavení je tento přepínač vypnutý. Řešení: vždy nastavte Json { ignoreUnknownKeys = true } pro produkční kód, abyste byli odolní vůči změnám API.
Druhá chyba — serializace internal nebo private polí v data class. V Kotlin data class jsou všechna pole v primárním konstruktoru ve výchozím nastavení serializována. Pokud pole obsahuje citlivá data (heslo, token), musíte jej označit @Transient nebo jej vyjmout z primárního konstruktoru. @Transient pole z JSON zcela vylučuje, ale v konstruktoru může způsobit chybu — je lepší takové pole definovat v těle třídy s @Transient.
Třetí chyba — polymorfní serializace bez sealed class. Pokud použijete open class místo sealed, kotlinx.serialization vyžaduje explicitní registraci všech potomků v serializersModule. Na rozdíl od sealed class, kde kompilátor zná všechny potomky, open class umožňuje libovolné rozšíření — knihovna nemůže automaticky určit všechny podtypy. Registrace se provádí přes Json { serializersModule = SerializersModule { polymorphic(Base::class) { subclass(Derived::class) } } }.
Verze kotlinx.serialization musí být kompatibilní s verzí Kotlin. JetBrains zveřejňuje tabulku kompatibility: kotlinx-serialization 1.6.x je kompatibilní s Kotlin 1.9.x, 1.7.x — s Kotlin 2.0.x a 2.1.x. Nesoulad verzí způsobuje záhadné chyby kompilace jako „Symbol ‘serializer’ is missing”. Vždy kontrolujte aktuální verzi na mavenCentral nebo v GitHub repozitáři projektu.
Často kladené otázky
kotlinx.serialization používá generování kódu v čase kompilace přes KSP, zatímco Gson a Moshi používají reflexi za běhu. To poskytuje výhodu ve výkonu (3-5krát rychlejší než Gson) a typové bezpečnosti. Gson serializuje jakékoli pole bez anotace, což může vést k úniku dat. kotlinx.serialization vyžaduje explicitní anotaci @Serializable, což je bezpečnější. Moshi také podporuje codegen, ale pouze pro JVM a Android.
Ano, kotlinx.serialization je oficiální multiplatformní knihovna JetBrains. Funguje na Kotlin/JVM (Android, Backend), Kotlin/Native (iOS), Kotlin/JS (Web, React) a Kotlin/Wasm. API je jednotné pro všechny platformy: @Serializable + Json.encodeToString() funguje všude stejně. Pro iOS nejsou vyžadována žádná další nastavení — Kotlin/Native zkompiluje serializovaný kód do nativního binárního souboru.
Nullable pole (String?) se deserializují jako null, pokud hodnota v JSON chybí nebo je uvedena jako null. Pro non-nullable pole (String) bez výchozí hodnoty způsobí absence pole v JSON SerializationException. Pokud chcete, aby null hodnoty nevstupovaly do JSON, nakonfigurujte Json { encodeDefaults = false }. To vyloučí z výstupu všechna pole rovna default (včetně null pro nullable).
Použijte @SerialName(„snake_case_name”) na každém poli, jehož název se liší od formátu Kotlin. Alternativně je pro Kotlin 2.0+ k dispozici Json { namingStrategy = JsonNamingStrategy.SnakeCase } — automatická konverze camelCase ↔ snake_case. Toto nastavení se aplikuje na všechna pole najednou. Pokud je vyžadováno částečné přizpůsobení, kombinujte @SerialName s globální strategií.
Ne, Flow a corutiny nejsou přímo serializovatelné — představují asynchronní provádění, nikoli data. Pro přenos dat z Flow je musíte shromáždit do kolekce pomocí .toList() v corutině a serializovat kolekci. Podobně nelze serializovat Job, Deferred nebo Continuation. Serializujte pouze data class — datové modely bez behaviorální logiky.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také