Upgrade kotlinx.serialization to v1.7.3 am: e895eb2f6a

Original change: https://android-review.googlesource.com/c/platform/external/kotlinx.serialization/+/3494856

Change-Id: Id9e066308f5d0b1436df077188cbc9eb520c3190
Signed-off-by: Automerger Merge Worker <[email protected]>
diff --git a/.gitignore b/.gitignore
index 054cf97..a21535c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,3 +20,6 @@
 
 # benchmarks.jar
 /benchmarks.jar
+
+# Intermediate klibs
+.kotlin
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 22d392c..2916b53 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,187 @@
+1.7.3 / 2024-09-19
+==================
+
+This release aims to fix important issues that were discovered in the 1.7.2 release,
+including the inability to sync certain projects into Android Studio/IntelliJ IDEA and exceptions from custom Uuid serializers.
+
+It uses Kotlin 2.0.20 by default.
+
+  * Use explicit kotlin-stdlib and kotlin-test versions from version catalog (#2818)
+  * Drop usage of deprecated Any?.freeze() in K/N target (#2819)
+  * Check against serialName instead of simpleClassName (#2802)
+  * Ignore NoClassDefFoundError when initializing builtins map for serializer() function (#2803)
+  * Clarify example for SerializationException (#2806)
+
+1.7.2 / 2024-08-28
+==================
+
+This release provides several new features, including a major Cbor configuration rework.
+It uses Kotlin 2.0.20 by default. 
+
+## Cbor feature set for COSE compliance
+
+This change brings a lot of features to the CBOR format, namely:
+
+- Serial Labels — see `@CborLabel` annotation and `preferCborLabelsOverNames` flag.
+- Tagging of keys and values — see `encode*Tags` and `verify*Tags` set of flags
+- Definite length encoding — see `useDefiniteLengthEncoding`. This flag affects object encoding, since decoding of arrays with definite lenghts is automatically supported.
+- Option to globally prefer major type 2 for byte array encoding — see `alwaysUseByteString` flag.
+
+Since there are quite a lot of flags now, they were restructured to a separate `CborConfiguration` class, similarly to `JsonConfiguration`.
+It is possible to retrieve this configuration from `CborEncoder/CborDecoder` interfaces in your custom serializers (see their documentation for details).
+
+All of these features make it possible to serialize and parse [COSE-compliant CBOR](https://datatracker.ietf.org/doc/html/rfc8152), for example, ISO/IEC 18013-5:2021-compliant mobile driving license data.
+In case you want to make use of them, there is a predefined `Cbor.CoseCompliant` instance.
+However, some canonicalization steps (such as sorting keys) still need to be performed manually. 
+
+This functionality [was contributed](https://github.com/Kotlin/kotlinx.serialization/pull/2412) to us by [Bernd Prünster](https://github.com/JesusMcCloud).
+
+## Keeping generated serializers
+
+One of the most requested features for serialization plugin was to continue to generate a serializer even if a custom one is specified for the class.
+It allows using a plugin-generated serializer in a fallback or delegate strategy, accessing type structure via descriptor, using default serialization behavior in inheritors that do not use custom serializers.
+
+Starting with this release, you can specify the `@KeepGeneratedSerializer` annotation on the class declaration to instruct the plugin to continue generating the serializer.
+In this case, the serializer will be accessible using the `.generatedSerializer()` function on the class's companion object.
+
+> This annotation is currently experimental. Kotlin 2.0.20 or higher is required for this feature to work.
+
+You can check out the examples in [the documentation](docs/serializers.md#simultaneous-use-of-plugin-generated-and-custom-serializers) and in the PRs: [#2758](https://github.com/Kotlin/kotlinx.serialization/pull/2758), [#2669](https://github.com/Kotlin/kotlinx.serialization/pull/2669).
+
+## Serializer for kotlin.uuid.Uuid
+
+Kotlin 2.0.20 [added](https://kotlinlang.org/docs/whatsnew2020.html#support-for-uuids-in-the-common-kotlin-standard-library) a common class to represent UUIDs in a multiplatform code.
+kotlinx.serialization 1.7.2 provides a corresponding `Uuid.serializer()` for it, making it possible to use it in `@Serializable` classes.
+Note that for now, serializer should be provided manually with [`@Contextual` annotation](https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/serializers.md#contextual-serialization).
+Plugin will be able to automatically insert `Uuid` serializer in Kotlin 2.1.0.
+
+See more details in the [corresponding PR](https://github.com/Kotlin/kotlinx.serialization/pull/2744).
+
+## Other bugfixes and improvements
+
+  * Prohibited using of zero and negative field numbers in ProtoNumber (#2766)
+  * Improve readability of protobuf decoding exception messages (#2768) (thanks to [xiaozhikang0916](https://github.com/xiaozhikang0916))
+  * docs(serializers): Fix grammatical errors (#2779) (thanks to [jamhour1g](https://github.com/jamhour1g))
+  * Fixed VerifyError after ProGuard optimization (#2728)
+  * Add wasm-wasi target to Okio integration (#2727)
+
+1.7.1 / 2024-06-25
+==================
+
+This is a bugfix release that aims to fix missing `kotlinx-serialization-hocon` artifact.
+It also contains experimental integration with `kotlinx-io` library.
+Kotlin 2.0.0 is used by default.
+
+## Fixed HOCON publication
+
+Sadly, 1.7.0 release was published incomplete: `kotlinx-serialization-hocon` artifact is missing from 1.7.0 and 1.7.0-RC releases.
+This release fixes this problem and now `kotlinx-serialization-hocon` is available again with 1.7.1 version.
+No other changes were made to this artifact. Related ticket: [#2717](https://github.com/Kotlin/kotlinx.serialization/issues/2717).
+
+## Add integration with a kotlinx-io library
+
+[`kotlinx-io`](https://github.com/Kotlin/kotlinx-io) is an official multiplatform library that provides basic IO primitives, similar to Okio. 
+kotlinx.serialization integration is now available in a separate artifact, located at the `kotlinx-serialization-json-io` coordinates.
+Integration artifact provides functions similar to existing [Okio integration](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json-okio/kotlinx.serialization.json.okio/): `encodeToSink`, `decodeFromSource`, and `decodeSourceToSequence`.
+Check out the [PR](https://github.com/Kotlin/kotlinx.serialization/pull/2707) for more details.
+
+## Other bugfixes
+  * Prohibited use of elements other than JsonObject in JsonTransformingSerializer with polymorphic serialization (#2715)
+
+1.7.0 / 2024-06-05
+==================
+
+This release contains all of the changes from 1.7.0-RC and is compatible with Kotlin 2.0.
+Please note that for reasons explained in the [1.7.0-RC changelog](https://github.com/Kotlin/kotlinx.serialization/releases/tag/v1.7.0-RC), it may not be possible to use it with the Kotlin 1.9.x
+compiler plugin. Yet, it is still fully backwards compatible with previous versions.
+
+The only difference with 1.7.0-RC is that `classDiscriminatorMode` property in `JsonBuilder` is marked as experimental,
+as it should have been when it was introduced (#2680).
+
+1.7.0-RC / 2024-05-16
+==================
+
+This is a release candidate for the next version. It is based on Kotlin 2.0.0-RC3 and is fully compatible with a stable Kotlin 2.0 release. 
+Due to a potential breaking change (see below), it requires a compiler plugin with a version at least of 2.0.0-RC1.
+
+### Important change: priority of PolymorphicSerializer for interfaces during call to serializer<T>() function
+
+Non-sealed interfaces in kotlinx.serialization are always [serializable with a polymorphic serializer](https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/polymorphism.md#serializing-interfaces),
+even if they do not have `@Serializable` annotation. This also means that `serializersModule.serializer<SomeInterface>()` call will return you a serializer capable of polymorphism.
+This function was written in a way that it unconditionally returns a `PolymorphicSerializer` if type argument is a non-sealed interface.
+This caused problems with `SerializersModule` functionality, because actual module was not taken into consideration, and therefore it was impossible
+to override serializer for interface using 'contextual serialization' feature. The problem is described in details [here](https://github.com/Kotlin/kotlinx.serialization/issues/2060).
+To overcome these problems, we had to change the behavior of this function regarding interfaces. It now looks into `SerializersModule` first if `T` is a non-sealed interface,
+and only if there is no registered contextual serializer for `T`, it returns a polymorphic serializer.
+
+Behavior **before 1.7.0-RC**:
+
+```kotlin
+interface SomeInterface
+
+val module = SerializersModule {
+    contextual(SomeInterface::class, CustomSomeInterfaceSerializer)
+}
+
+// Prints PolymorphicSerializer<SomeInterface>:
+println(module.serializer<SomeInterface>())
+```
+
+Behavior **in 1.7.0-RC, 1.7.0, and higher**:
+
+```kotlin
+interface SomeInterface
+
+val module = SerializersModule {
+    contextual(SomeInterface::class, CustomSomeInterfaceSerializer)
+}
+
+// Prints CustomSomeInterfaceSerializer:
+println(module.serializer<SomeInterface>())
+```
+
+We expect minimal impact from this change but be aware of it anyway.
+Implementation details are available in [this PR](https://github.com/Kotlin/kotlinx.serialization/issues/2060).
+
+Due to the [serializer() function being also a compiler intrinsic](https://github.com/Kotlin/kotlinx.serialization/issues/1348), code
+of kotlinx.serialization compiler plugin also accommodates for this change in 2.0 branch. To get a consistent result from both plugin and runtime,
+kotlinx.serialization compiler plugin should be **at least of 2.0.0-RC1 version.** 
+**To verify so, 1.7.0-RC runtime will be rejected by older plugins.**
+
+### Json configuration flag to allow commentaries
+
+While JSON standard does not allow any kind of commentaries, they are one of the most popular extensions — for example,
+commentaries are widely used in configuration files.
+To support this use-case, we added a new configuration flag, `allowComments`.
+This flag allows the parser to skip over C/Java-style commentaries in JSON input.
+Note that commentaries cannot affect decoding or encoding in any way and are not stored anywhere.
+See details in [the PR](https://github.com/Kotlin/kotlinx.serialization/pull/2592).
+
+### Promote `JsonConfiguration.explicitNulls` to a stable API
+
+This configuration flag has been around for a long time and got positive feedback.
+Therefore, we are promoting it to a stable state.
+It also received functionality enhancements when used with `JsonConfiguration.coerceInputValues` ([#2586](https://github.com/Kotlin/kotlinx.serialization/issues/2586)).
+See related [PR](https://github.com/Kotlin/kotlinx.serialization/pull/2661) for details.
+
+### `oneof` support in ProtoBuf
+
+`oneof` fields in protobuf messages [represent a set of optional fields](https://protobuf.dev/programming-guides/proto2/#oneof), where the only one of them is present.
+With the help of the new `@ProtoOneOf` annotation, you can naturally map them to Kotlin's sealed class hierarchy.
+Check out the comprehensive guide for this feature [here](https://github.com/Kotlin/kotlinx.serialization/blob/194a188563c612c63a88271eb3f28f37353df514/docs/formats.md#oneof-field-experimental).
+
+This functionality was [contributed](https://github.com/Kotlin/kotlinx.serialization/pull/2546) to us by [xzk](https://github.com/xiaozhikang0916).
+
+### Other improvements and bugfixes
+
+* Update okio to 3.9.0 version (#2671)
+* Add extension to access original descriptor from one made with SerialDescriptor.nullable (#2633) (thanks to [Chuckame](https://github.com/Chuckame))
+* Use @SerialName of inline polymorphic children in Json (#2601) (thanks to [Tad Fisher](https://github.com/tadfisher))
+* Fix serializing nulls for a property of a parameterized type with a nullable upper bound with Protobuf (#2561) (thanks to [Shreck Ye](https://github.com/ShreckYe))
+* Fixed type discriminator value for custom serializer that uses `encodeJsonElement` (#2628)
+* Refine exception messages in case of deserializing data from JsonElement. (#2648)
+
+
 1.6.3 / 2024-02-16
 ==================
 
diff --git a/METADATA b/METADATA
index f9d2338..3ef55da 100644
--- a/METADATA
+++ b/METADATA
@@ -1,20 +1,20 @@
 # This project was upgraded with external_updater.
 # Usage: tools/external_updater/updater.sh update external/kotlinx.serialization
-# For more info, check https://cs.android.com/android/platform/superproject/+/main:tools/external_updater/README.md
+# For more info, check https://cs.android.com/android/platform/superproject/main/+/main:tools/external_updater/README.md
 
 name: "kotlinx.serialization"
 description: "Kotlin serialization consists of a compiler plugin, that generates visitor code for serializable classes, runtime library with core serialization API and support libraries with various serialization formats."
 third_party {
   license_type: NOTICE
   last_upgrade_date {
-    year: 2024
-    month: 4
-    day: 22
+    year: 2025
+    month: 2
+    day: 17
   }
   identifier {
     type: "Git"
     value: "https://github.com/Kotlin/kotlinx.serialization"
-    version: "v1.6.3"
+    version: "v1.7.3"
     primary_source: true
   }
 }
diff --git a/README.md b/README.md
index d69420a..c451e41 100644
--- a/README.md
+++ b/README.md
@@ -4,8 +4,8 @@
 [![JetBrains official project](https://jb.gg/badges/official.svg)](https://confluence.jetbrains.com/display/ALL/JetBrains+on+GitHub)
 [![GitHub license](https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg?style=flat)](http://www.apache.org/licenses/LICENSE-2.0)
 [![TeamCity build](https://img.shields.io/teamcity/http/teamcity.jetbrains.com/s/KotlinTools_KotlinxSerialization_Ko.svg)](https://teamcity.jetbrains.com/viewType.html?buildTypeId=KotlinTools_KotlinxSerialization_Ko&guest=1)
-[![Kotlin](https://img.shields.io/badge/kotlin-1.9.22-blue.svg?logo=kotlin)](http://kotlinlang.org)
-[![Maven Central](https://img.shields.io/maven-central/v/org.jetbrains.kotlinx/kotlinx-serialization-core/1.6.3)](https://central.sonatype.com/artifact/org.jetbrains.kotlinx/kotlinx-serialization-core/1.6.3)
+[![Kotlin](https://img.shields.io/badge/kotlin-2.0.20-blue.svg?logo=kotlin)](http://kotlinlang.org)
+[![Maven Central](https://img.shields.io/maven-central/v/org.jetbrains.kotlinx/kotlinx-serialization-core/1.7.3)](https://central.sonatype.com/artifact/org.jetbrains.kotlinx/kotlinx-serialization-core/1.7.3)
 [![KDoc link](https://img.shields.io/badge/API_reference-KDoc-blue)](https://kotlinlang.org/api/kotlinx.serialization/)
 [![Slack channel](https://img.shields.io/badge/chat-slack-blue.svg?logo=slack)](https://kotlinlang.slack.com/messages/serialization/)
 
@@ -95,8 +95,8 @@
 
 ```kotlin
 plugins {
-    kotlin("jvm") version "1.9.22" // or kotlin("multiplatform") or any other kotlin plugin
-    kotlin("plugin.serialization") version "1.9.22"
+    kotlin("jvm") version "2.0.20" // or kotlin("multiplatform") or any other kotlin plugin
+    kotlin("plugin.serialization") version "2.0.20"
 }
 ```       
 
@@ -104,8 +104,8 @@
 
 ```gradle
 plugins {
-    id 'org.jetbrains.kotlin.multiplatform' version '1.9.22'
-    id 'org.jetbrains.kotlin.plugin.serialization' version '1.9.22'
+    id 'org.jetbrains.kotlin.multiplatform' version '2.0.20'
+    id 'org.jetbrains.kotlin.plugin.serialization' version '2.0.20'
 }
 ```
 
@@ -123,7 +123,7 @@
     repositories { mavenCentral() }
 
     dependencies {
-        val kotlinVersion = "1.9.22"
+        val kotlinVersion = "2.0.20"
         classpath(kotlin("gradle-plugin", version = kotlinVersion))
         classpath(kotlin("serialization", version = kotlinVersion))
     }
@@ -134,7 +134,7 @@
 
 ```gradle
 buildscript {
-    ext.kotlin_version = '1.9.22'
+    ext.kotlin_version = '2.0.20'
     repositories { mavenCentral() }
 
     dependencies {
@@ -164,7 +164,7 @@
 }
 
 dependencies {
-    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3")
+    implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3")
 }
 ```
 
@@ -176,7 +176,7 @@
 }
 
 dependencies {
-    implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3"
+    implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.3"
 }
 ```
 
@@ -266,8 +266,8 @@
 
 ```xml
 <properties>
-    <kotlin.version>1.9.22</kotlin.version>
-    <serialization.version>1.6.3</serialization.version>
+    <kotlin.version>2.0.20</kotlin.version>
+    <serialization.version>1.7.3</serialization.version>
 </properties>
 ```
 
diff --git a/RELEASING.md b/RELEASING.md
index f8b90e9..561fa0c 100644
--- a/RELEASING.md
+++ b/RELEASING.md
@@ -13,7 +13,7 @@
    * [`gradle.properties`](gradle.properties)
    * [`integration-test/gradle.properties`](integration-test/gradle.properties)
 
-   Update Kotlin version, if necessary.
+   Update Kotlin version in [`libs.versions.toml`](gradle/libs.versions.toml), if necessary.
 
 5. Write release notes in [`CHANGELOG.md`](CHANGELOG.md):
    * Use old releases as example of style.
diff --git a/benchmark/build.gradle b/benchmark/build.gradle
deleted file mode 100644
index 751ad78..0000000
--- a/benchmark/build.gradle
+++ /dev/null
@@ -1,52 +0,0 @@
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
-
-/*
- * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-apply plugin: 'java'
-apply plugin: 'kotlin'
-apply plugin: 'kotlinx-serialization'
-apply plugin: 'idea'
-apply plugin: 'com.github.johnrengelman.shadow'
-apply plugin: 'me.champeau.jmh'
-
-sourceCompatibility = 1.8
-targetCompatibility = 1.8
-jmh.jmhVersion = "1.35"
-
-processJmhResources {
-    doFirst {
-        duplicatesStrategy(DuplicatesStrategy.EXCLUDE)
-    }
-}
-
-jmhJar {
-    archiveBaseName.set('benchmarks')
-    archiveVersion.set('')
-    destinationDirectory = file("$rootDir")
-}
-
-// to include benchmark-module jmh source set compilation during build to verify that it is also compiled succesfully
-assemble.dependsOn jmhClasses
-
-tasks.withType(KotlinCompile).configureEach {
-    kotlinOptions {
-        if (rootProject.ext.kotlin_lv_override != null) {
-            languageVersion = rootProject.ext.kotlin_lv_override
-            freeCompilerArgs += "-Xsuppress-version-warnings"
-        }
-    }
-}
-
-dependencies {
-    implementation 'org.openjdk.jmh:jmh-core:1.35'
-    implementation 'com.google.guava:guava:31.1-jre'
-    implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.3'
-    implementation 'com.fasterxml.jackson.module:jackson-module-kotlin:2.13.3'
-    implementation "com.squareup.okio:okio:$okio_version"
-    implementation project(':kotlinx-serialization-core')
-    implementation project(':kotlinx-serialization-json')
-    implementation project(':kotlinx-serialization-json-okio')
-    implementation project(':kotlinx-serialization-protobuf')
-}
diff --git a/benchmark/build.gradle.kts b/benchmark/build.gradle.kts
new file mode 100644
index 0000000..1f5585c
--- /dev/null
+++ b/benchmark/build.gradle.kts
@@ -0,0 +1,71 @@
+import org.gradle.kotlin.dsl.support.*
+import org.jetbrains.kotlin.gradle.dsl.*
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+plugins {
+    java
+    idea
+    kotlin("jvm")
+    alias(libs.plugins.serialization)
+    alias(libs.plugins.shadow)
+    alias(libs.plugins.jmh)
+    kotlin("kapt") // For annotation processing
+}
+
+java {
+    sourceCompatibility = JavaVersion.VERSION_1_8
+    targetCompatibility = JavaVersion.VERSION_1_8
+}
+
+jmh {
+    jmhVersion.set("1.35")
+}
+
+tasks.processJmhResources {
+    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
+}
+
+tasks.jmhJar {
+    archiveBaseName.set("benchmarks")
+    archiveVersion.set("")
+    archiveClassifier.set("") // benchmarks.jar, not benchmarks-jmh.jar
+    destinationDirectory.set(file("$rootDir"))
+}
+
+// to include benchmark-module jmh source set compilation during build to verify that it is also compiled succesfully
+tasks.assemble {
+    dependsOn(tasks.jmhClasses)
+}
+
+kotlin {
+    compilerOptions {
+        jvmTarget = JvmTarget.JVM_1_8
+        if (overriddenLanguageVersion != null) {
+            languageVersion = KotlinVersion.fromVersion(overriddenLanguageVersion!!)
+            freeCompilerArgs.add("-Xsuppress-version-warnings")
+        }
+    }
+}
+
+dependencies {
+    implementation(libs.jmhCore)
+    implementation(libs.guava)
+    implementation(libs.jackson.databind)
+    implementation(libs.jackson.module.kotlin)
+    implementation(libs.okio)
+    implementation(libs.kotlinx.io)
+    implementation(project(":kotlinx-serialization-core"))
+    implementation(project(":kotlinx-serialization-cbor"))
+    implementation(project(":kotlinx-serialization-json"))
+    implementation(project(":kotlinx-serialization-json-okio"))
+    implementation(project(":kotlinx-serialization-json-io"))
+    implementation(project(":kotlinx-serialization-protobuf"))
+
+    // Moshi
+    implementation(libs.moshi.kotlin)
+    kapt(libs.moshi.codegen)
+}
diff --git a/benchmark/src/jmh/kotlin/kotlinx/benchmarks/cbor/CborBaseLine.kt b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/cbor/CborBaseLine.kt
new file mode 100644
index 0000000..8b71d92
--- /dev/null
+++ b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/cbor/CborBaseLine.kt
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.benchmarks.cbor
+
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.cbor.*
+import org.openjdk.jmh.annotations.*
+import java.util.concurrent.*
+
+@Serializable
+data class KTestAllTypes(
+        val i32: Int,
+        val i64: Long,
+        val f: Float,
+        val d: Double,
+        val s: String,
+        val b: Boolean = false,
+    )
+
+@Serializable
+data class KTestOuterMessage(
+        val a: Int,
+        val b: Double,
+        val inner: KTestAllTypes,
+        val s: String,
+        val ss: List<String>
+    )
+
+@Warmup(iterations = 5, time = 1)
+@Measurement(iterations = 10, time = 1)
+@BenchmarkMode(Mode.Throughput)
+@OutputTimeUnit(TimeUnit.MILLISECONDS)
+@State(Scope.Benchmark)
+@Fork(1)
+open class CborBaseline {
+        val baseMessage = KTestOuterMessage(
+                42,
+                256123123412.0,
+                s = "string",
+                ss = listOf("a", "b", "c"),
+                inner = KTestAllTypes(-123124512, 36253671257312, Float.MIN_VALUE, -23e15, "foobarbaz")
+                )
+
+        val cbor = Cbor {
+                encodeDefaults = true
+                encodeKeyTags = false
+                encodeValueTags = false
+                useDefiniteLengthEncoding = false
+                preferCborLabelsOverNames = false
+            }
+
+        val baseBytes = cbor.encodeToByteArray(KTestOuterMessage.serializer(), baseMessage)
+
+        @Benchmark
+        fun toBytes() = cbor.encodeToByteArray(KTestOuterMessage.serializer(), baseMessage)
+
+        @Benchmark
+        fun fromBytes() = cbor.decodeFromByteArray(KTestOuterMessage.serializer(), baseBytes)
+
+    }
diff --git a/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/JacksonComparisonBenchmark.kt b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/JacksonComparisonBenchmark.kt
index d162418..2daaee0 100644
--- a/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/JacksonComparisonBenchmark.kt
+++ b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/JacksonComparisonBenchmark.kt
@@ -2,6 +2,7 @@
 
 import com.fasterxml.jackson.databind.*
 import com.fasterxml.jackson.module.kotlin.*
+import benchmarks.model.*
 import kotlinx.serialization.*
 import kotlinx.serialization.json.*
 import kotlinx.serialization.json.okio.encodeToBufferedSink
@@ -20,53 +21,8 @@
 @Fork(2)
 open class JacksonComparisonBenchmark {
 
-    @Serializable
-    data class DefaultPixelEvent(
-        val version: Int,
-        val dateTime2: String,
-        val serverName: String,
-        val domain: String,
-        val method: String,
-        val clientIp: String,
-        val queryString: String,
-        val userAgent: String,
-        val contentType: String,
-        val browserLanguage: String,
-        val postData: String,
-        val cookies: String
-    )
-
     private val objectMapper: ObjectMapper = jacksonObjectMapper()
 
-    private val data = DefaultPixelEvent(
-        version = 1,
-        dateTime2 = System.currentTimeMillis().toString(),
-        serverName = "some-endpoint-qwer",
-        domain = "some.domain.com",
-        method = "POST",
-        clientIp = "127.0.0.1",
-        queryString = "anxa=CASCative&anxv=13.901.16.34566&anxe=FoolbarActive&anxt=E7AFBF15-1761-4343-92C1-78167ED19B1C&anxtv=13.901.16.34566&anxp=%5ECQ6%5Expt292%5ES33656%5Eus&anxsi&anxd=2019-10-08T17%3A03%3A57.246Z&f=00400000&anxr=1571945992297&coid=66abafd0d49f42e58dc7536109395306&userSegment&cwsid=opgkcnbminncdgghighmimmphiooeohh",
-        userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:70.0) Gecko/20100101 Firefox/70.0",
-        contentType = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
-        browserLanguage = "en-US,en;q=0.5",
-        postData = "-",
-        cookies = "_ga=GA1.2.971852807.1546968515"
-    )
-
-    private val dataWithEscapes = DefaultPixelEvent(
-        version = 1,
-        dateTime2 = System.currentTimeMillis().toString(),
-        serverName = "some-endp\"oint-qwer",
-        domain = "<a href=\"some.domain.com\">",
-        method = "POST",
-        clientIp = "127.0.0.1",
-        queryString = "anxa=CASCative&anxv=13.901.16.34566&anxe=\"FoolbarActive\"&anxt=E7AFBF15-1761-4343-92C1-78167ED19B1C&anxtv=13.901.16.34566&anxp=%5ECQ6%5Expt292%5ES33656%5Eus&anxsi&anxd=2019-10-08T17%3A03%3A57.246Z&f=00400000&anxr=1571945992297&coid=\"66abafd0d49f42e58dc7536109395306\"&userSegment&cwsid=opgkcnbminncdgghighmimmphiooeohh",
-        userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:70.0) Gecko/20100101 Firefox/70.0",
-        contentType = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
-        browserLanguage = "\"en\"-\"US\",en;\\q=0.5",
-        postData = "-",
-        cookies = "_ga=GA1.2.971852807.1546968515"
-    )
 
     private val devNullSink = blackholeSink().buffer()
     private val devNullStream = object : OutputStream() {
@@ -75,7 +31,7 @@
         override fun write(b: ByteArray, off: Int, len: Int) {}
     }
 
-    private val stringData = Json.encodeToString(DefaultPixelEvent.serializer(), data)
+    private val stringData = Json.encodeToString(DefaultPixelEvent.serializer(), pixelEvent)
     private val utf8BytesData = stringData.toByteArray()
 
     @Serializable
@@ -84,28 +40,28 @@
     private val smallData = SmallDataClass(42, "Vincent")
 
     @Benchmark
-    fun jacksonToString(): String = objectMapper.writeValueAsString(data)
+    fun jacksonToString(): String = objectMapper.writeValueAsString(pixelEvent)
 
     @Benchmark
-    fun jacksonToStringWithEscapes(): String = objectMapper.writeValueAsString(dataWithEscapes)
+    fun jacksonToStringWithEscapes(): String = objectMapper.writeValueAsString(pixelEventWithEscapes)
 
     @Benchmark
     fun jacksonSmallToString(): String = objectMapper.writeValueAsString(smallData)
 
     @Benchmark
-    fun kotlinToString(): String = Json.encodeToString(DefaultPixelEvent.serializer(), data)
+    fun kotlinToString(): String = Json.encodeToString(DefaultPixelEvent.serializer(), pixelEvent)
 
     @Benchmark
-    fun kotlinToStream() = Json.encodeToStream(DefaultPixelEvent.serializer(), data, devNullStream)
+    fun kotlinToStream() = Json.encodeToStream(DefaultPixelEvent.serializer(), pixelEvent, devNullStream)
 
     @Benchmark
     fun kotlinFromStream() = Json.decodeFromStream(DefaultPixelEvent.serializer(), ByteArrayInputStream(utf8BytesData))
 
     @Benchmark
-    fun kotlinToOkio() = Json.encodeToBufferedSink(DefaultPixelEvent.serializer(), data, devNullSink)
+    fun kotlinToOkio() = Json.encodeToBufferedSink(DefaultPixelEvent.serializer(), pixelEvent, devNullSink)
 
     @Benchmark
-    fun kotlinToStringWithEscapes(): String = Json.encodeToString(DefaultPixelEvent.serializer(), dataWithEscapes)
+    fun kotlinToStringWithEscapes(): String = Json.encodeToString(DefaultPixelEvent.serializer(), pixelEventWithEscapes)
 
     @Benchmark
     fun kotlinSmallToString(): String = Json.encodeToString(SmallDataClass.serializer(), smallData)
diff --git a/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/TwitterFeedCommentsBenchmark.kt b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/TwitterFeedCommentsBenchmark.kt
new file mode 100644
index 0000000..52cb748
--- /dev/null
+++ b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/TwitterFeedCommentsBenchmark.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.benchmarks.json
+
+import kotlinx.benchmarks.model.*
+import kotlinx.serialization.json.*
+import org.openjdk.jmh.annotations.*
+import java.io.*
+import java.util.concurrent.*
+
+@Warmup(iterations = 7, time = 1)
+@Measurement(iterations = 7, time = 1)
+@BenchmarkMode(Mode.Throughput)
+@OutputTimeUnit(TimeUnit.SECONDS)
+@State(Scope.Benchmark)
+@Fork(2)
+open class TwitterFeedCommentsBenchmark {
+    val inputBytes = TwitterFeedBenchmark::class.java.getResource("/twitter_macro.json").readBytes()
+    private val input = inputBytes.decodeToString()
+    private val inputWithComments = prepareInputWithComments(input)
+    private val inputWithCommentsBytes = inputWithComments.encodeToByteArray()
+
+    private val jsonComments = Json { ignoreUnknownKeys = true; allowComments = true; }
+    private val jsonNoComments = Json { ignoreUnknownKeys = true; allowComments = false; }
+
+    fun prepareInputWithComments(inp: String): String {
+        val result = inp.lineSequence().map { s ->
+            // "id", "in_...", "is_...", etc
+            if (!s.trimStart().startsWith("\"i")) s else "$s // json comment"
+        }.joinToString("\n")
+        assert(result.contains("// json comment"))
+        return result
+    }
+
+    @Setup
+    fun init() {
+        // Explicitly invoking both variants before benchmarking so we know that both parser implementation classes are loaded
+        require("foobar" == jsonComments.decodeFromString<String>("\"foobar\""))
+        require("foobar" == jsonNoComments.decodeFromString<String>("\"foobar\""))
+    }
+
+    // The difference with TwitterFeedBenchmark.decodeMicroTwitter shows if we slow down when both StringJsonLexer and CommentsJsonLexer
+    // are loaded by JVM. Should be almost non-existent on modern JVMs (but on OpenJDK-Corretto-11.0.14.1 there is one. 17 is fine.)
+    @Benchmark
+    fun decodeMicroTwitter() = jsonNoComments.decodeFromString(MicroTwitterFeed.serializer(), input)
+
+    // The difference with this.decodeMicroTwitter shows if we slow down when comments are enabled but no comments present
+    // in the input. It is around 13% slower than without comments support, mainly because skipWhitespaces is a separate function
+    // that sometimes is not inlined by JIT.
+    @Benchmark
+    fun decodeMicroTwitterCommentSupport() = jsonComments.decodeFromString(MicroTwitterFeed.serializer(), input)
+
+    // Shows how much actual skipping of the comments takes: around 10%.
+    @Benchmark
+    fun decodeMicroTwitterCommentInData() = jsonComments.decodeFromString(MicroTwitterFeed.serializer(), inputWithComments)
+
+    @Benchmark
+    fun decodeMicroTwitterCommentSupportStream() = jsonComments.decodeFromStream(MicroTwitterFeed.serializer(), ByteArrayInputStream(inputBytes))
+
+    @Benchmark
+    fun decodeMicroTwitterCommentInDataStream() = jsonComments.decodeFromStream(MicroTwitterFeed.serializer(), ByteArrayInputStream(inputWithCommentsBytes))
+}
diff --git a/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/TwitterFeedStreamBenchmark.kt b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/TwitterFeedStreamBenchmark.kt
index 0d9b4cf..c110428 100644
--- a/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/TwitterFeedStreamBenchmark.kt
+++ b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/TwitterFeedStreamBenchmark.kt
@@ -5,14 +5,17 @@
 import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
 import kotlinx.benchmarks.model.MacroTwitterFeed
 import kotlinx.benchmarks.model.MicroTwitterFeed
+import kotlinx.io.*
 import kotlinx.serialization.json.*
+import kotlinx.serialization.json.io.*
+import kotlinx.serialization.json.okio.*
+import okio.*
 import org.openjdk.jmh.annotations.*
 import java.io.*
-import java.nio.file.Files
-import java.nio.file.Path
 import java.util.concurrent.TimeUnit
-import kotlin.io.path.deleteIfExists
-import kotlin.io.path.outputStream
+import kotlin.io.use
+import okio.Buffer as OkioBuffer
+import okio.Sink as OkioSink
 
 @Warmup(iterations = 7, time = 1)
 @Measurement(iterations = 7, time = 1)
@@ -30,6 +33,14 @@
         jacksonObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
 
 
+    @Setup
+    fun init() {
+        // Explicitly invoking decodeFromStream before benchmarking so we know that both parser implementation classes are loaded
+        require("foobar" == Json.decodeFromStream<String>(ByteArrayInputStream("\"foobar\"".encodeToByteArray())))
+        require("foobar" == Json.decodeFromString<String>("\"foobar\""))
+    }
+
+
     private val inputStream: InputStream
         get() = ByteArrayInputStream(bytes)
     private val outputStream: OutputStream
@@ -60,6 +71,26 @@
     }
 
     @Benchmark
+    fun encodeTwitterOkioStream(): OkioSink {
+        val b = OkioBuffer()
+        Json.encodeToBufferedSink(MacroTwitterFeed.serializer(), twitter, b)
+        return b
+    }
+
+    @Benchmark
+    fun encodeTwitterKotlinxIoStream(): Sink {
+        val b = Buffer()
+        Json.encodeToSink(MacroTwitterFeed.serializer(), twitter, b)
+        return b
+    }
+
+    /**
+     * While encode* benchmarks use MacroTwitterFeed model to output as many bytes as possible,
+     * decode* benchmarks use MicroTwitterFeed model to also factor for skipping over unnecessary data.
+     */
+
+    // Difference with TwitterFeedBenchmark.decodeMicroTwitter shows how heavy Java's standard UTF-8 decoding actually is.
+    @Benchmark
     fun decodeMicroTwitterReadText(): MicroTwitterFeed {
         return inputStream.use {
             jsonIgnoreUnknwn.decodeFromString(MicroTwitterFeed.serializer(), it.bufferedReader().readText())
@@ -79,4 +110,24 @@
             objectMapper.readValue(it, MicroTwitterFeed::class.java)
         }
     }
+
+    @Benchmark
+    fun decodeMicroTwitterOkioStream(): MicroTwitterFeed {
+        // It seems there is no optimal way to reuse `bytes` between benchmark, so we are forced
+        // to write them to buffer every time.
+        // Note that it makes comparison with Jackson and InputStream integration much less meaningful.
+        val b = OkioBuffer()
+        b.write(bytes)
+        return jsonIgnoreUnknwn.decodeFromBufferedSource(MicroTwitterFeed.serializer(), b)
+    }
+
+    @Benchmark
+    fun decodeMicroTwitterKotlinxIoStream(): MicroTwitterFeed {
+        // It seems there is no way to reuse filled buffer between benchmark iterations, so we are forced
+        // to write bytes to buffer every time.
+        // Note that it makes comparison with Jackson and InputStream integration much less meaningful.
+        val b = Buffer()
+        b.write(bytes)
+        return jsonIgnoreUnknwn.decodeFromSource(MicroTwitterFeed.serializer(), b)
+    }
 }
diff --git a/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/moshi/MoshiBaseline.kt b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/moshi/MoshiBaseline.kt
new file mode 100644
index 0000000..941a570
--- /dev/null
+++ b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/json/moshi/MoshiBaseline.kt
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.benchmarks.json.moshi
+
+
+import com.squareup.moshi.*
+import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
+import benchmarks.model.*import kotlinx.serialization.json.*
+import kotlinx.serialization.json.Json
+import kotlinx.serialization.json.okio.*
+import okio.*
+import org.openjdk.jmh.annotations.*
+import java.io.*
+import java.util.concurrent.*
+
+
+/**
+ * Note: these benchmarks were not checked to compare anything meaningful.
+ * It's just a baseline to simplify Moshi configuration for a more intricated comparisons.
+ * // M3, 1.7.1, Corretto 17.0.7
+ *
+ * Benchmark                        Mode  Cnt     Score    Error   Units
+ * MoshiBaseline.kotlinFromSource  thrpt    5   283.587 ± 10.556  ops/ms
+ * MoshiBaseline.kotlinFromString  thrpt    5  1518.012 ± 47.191  ops/ms
+ * MoshiBaseline.kotlinToOkio      thrpt    5  1055.492 ± 48.782  ops/ms
+ * MoshiBaseline.kotlinToString    thrpt    5  2264.407 ± 88.324  ops/ms
+ *
+ * MoshiBaseline.moshiFromSource   thrpt    5  1280.841 ± 81.180  ops/ms
+ * MoshiBaseline.moshiFromString   thrpt    5  1137.416 ± 63.516  ops/ms
+ * MoshiBaseline.moshiToOkio       thrpt    5   963.130 ± 50.316  ops/ms
+ * MoshiBaseline.moshiToString     thrpt    5  1061.251 ± 10.217  ops/ms
+ */
+@Warmup(iterations = 5, time = 1)
+@Measurement(iterations = 5, time = 1)
+@BenchmarkMode(Mode.Throughput)
+@OutputTimeUnit(TimeUnit.MILLISECONDS)
+@State(Scope.Benchmark)
+@Fork(1)
+open class MoshiBaseline {
+
+    private val moshi = Moshi.Builder()
+        .add(KotlinJsonAdapterFactory())
+        .build()
+
+    private val jsonAdapter = moshi.adapter(DefaultPixelEvent::class.java)
+
+    private val devNullSink = blackholeSink().buffer()
+
+    private val source = Buffer().writeUtf8(pixelEventJson)
+
+    // Moshi
+
+    @Benchmark
+    fun moshiToString(): String = jsonAdapter.toJson(pixelEvent)
+
+    @Benchmark
+    fun moshiToOkio() = jsonAdapter.toJson(devNullSink, pixelEvent)
+
+    @Benchmark
+    fun moshiFromString(): DefaultPixelEvent = jsonAdapter.fromJson(pixelEventJson)!!
+
+    @Benchmark
+    fun moshiFromSource(): DefaultPixelEvent = jsonAdapter.fromJson(source.copy())!!
+
+    // Kx
+
+    @Benchmark
+    fun kotlinToString(): String = Json.encodeToString(DefaultPixelEvent.serializer(), pixelEvent)
+
+    @Benchmark
+    fun kotlinToOkio() = Json.encodeToBufferedSink(DefaultPixelEvent.serializer(), pixelEvent, devNullSink)
+
+    @Benchmark
+    fun kotlinFromString(): DefaultPixelEvent = Json.decodeFromString(DefaultPixelEvent.serializer(), pixelEventJson)
+
+    @Benchmark
+    fun kotlinFromSource(): DefaultPixelEvent = Json.decodeFromBufferedSource(DefaultPixelEvent.serializer(), source.copy())
+}
diff --git a/benchmark/src/jmh/kotlin/kotlinx/benchmarks/model/PixelEvents.kt b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/model/PixelEvents.kt
new file mode 100644
index 0000000..6854f93
--- /dev/null
+++ b/benchmark/src/jmh/kotlin/kotlinx/benchmarks/model/PixelEvents.kt
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package benchmarks.model // NOT A KOTLINX PACKAGE. Otherwise Moshi starts complaining on platform classes
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+// For Jackson/Moshi data
+
+
+@Serializable
+data class DefaultPixelEvent(
+    val version: Int,
+    val dateTime2: String,
+    val serverName: String,
+    val domain: String,
+    val method: String,
+    val clientIp: String,
+    val queryString: String,
+    val userAgent: String,
+    val contentType: String,
+    val browserLanguage: String,
+    val postData: String,
+    val cookies: String
+)
+
+val pixelEvent = DefaultPixelEvent(
+    version = 1,
+    dateTime2 = System.currentTimeMillis().toString(),
+    serverName = "some-endpoint-qwer",
+    domain = "some.domain.com",
+    method = "POST",
+    clientIp = "127.0.0.1",
+    queryString = "anxa=CASCative&anxv=13.901.16.34566&anxe=FoolbarActive&anxt=E7AFBF15-1761-4343-92C1-78167ED19B1C&anxtv=13.901.16.34566&anxp=%5ECQ6%5Expt292%5ES33656%5Eus&anxsi&anxd=2019-10-08T17%3A03%3A57.246Z&f=00400000&anxr=1571945992297&coid=66abafd0d49f42e58dc7536109395306&userSegment&cwsid=opgkcnbminncdgghighmimmphiooeohh",
+    userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:70.0) Gecko/20100101 Firefox/70.0",
+    contentType = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
+    browserLanguage = "en-US,en;q=0.5",
+    postData = "-",
+    cookies = "_ga=GA1.2.971852807.1546968515"
+)
+
+val pixelEventJson = Json.encodeToString(pixelEvent)
+
+val pixelEventWithEscapes = DefaultPixelEvent(
+    version = 1,
+    dateTime2 = System.currentTimeMillis().toString(),
+    serverName = "some-endp\"oint-qwer",
+    domain = "<a href=\"some.domain.com\">",
+    method = "POST",
+    clientIp = "127.0.0.1",
+    queryString = "anxa=CASCative&anxv=13.901.16.34566&anxe=\"FoolbarActive\"&anxt=E7AFBF15-1761-4343-92C1-78167ED19B1C&anxtv=13.901.16.34566&anxp=%5ECQ6%5Expt292%5ES33656%5Eus&anxsi&anxd=2019-10-08T17%3A03%3A57.246Z&f=00400000&anxr=1571945992297&coid=\"66abafd0d49f42e58dc7536109395306\"&userSegment&cwsid=opgkcnbminncdgghighmimmphiooeohh",
+    userAgent = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:70.0) Gecko/20100101 Firefox/70.0",
+    contentType = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
+    browserLanguage = "\"en\"-\"US\",en;\\q=0.5",
+    postData = "-",
+    cookies = "_ga=GA1.2.971852807.1546968515"
+)
diff --git a/bom/build.gradle b/bom/build.gradle
deleted file mode 100644
index 12e2f6b..0000000
--- a/bom/build.gradle
+++ /dev/null
@@ -1,41 +0,0 @@
-plugins {
-    id 'java-platform'
-}
-
-def name = project.name
-
-dependencies {
-    constraints {
-        rootProject.subprojects.each {
-            if (it.name == name) return
-            if (!it.plugins.hasPlugin('maven-publish')) return
-            evaluationDependsOn(it.path)
-            it.publishing.publications.all {
-                if (it.artifactId.endsWith("-kotlinMultiplatform")) return
-                if (it.artifactId.endsWith("-metadata")) return
-                // Skip platform artifacts (like *-linuxx64, *-macosx64)
-                // It leads to inconsistent bom when publishing from different platforms
-                // (e.g. on linux it will include only linuxx64 artifacts and no macosx64)
-                // It shouldn't be a problem as usually consumers need to use generic *-native artifact
-                // Gradle will choose correct variant by using metadata attributes
-                if (it.artifacts.any { it.extension == 'klib' }) return
-                api("${it.groupId}:${it.artifactId}:${it.version}")
-            }
-        }
-    }
-}
-
-publishing {
-    publications {
-        mavenBom(MavenPublication) {
-            from components.javaPlatform
-        }
-        // Disable metadata publication, no need to
-        it.each { pub ->
-            pub.moduleDescriptorGenerator = null
-            tasks.matching { it.name == "generateMetadataFileFor${pub.name.capitalize()}Publication" }.all {
-                onlyIf { false }
-            }
-        }
-    }
-}
diff --git a/bom/build.gradle.kts b/bom/build.gradle.kts
new file mode 100644
index 0000000..7e40b92
--- /dev/null
+++ b/bom/build.gradle.kts
@@ -0,0 +1,51 @@
+import org.gradle.api.publish.maven.internal.publication.DefaultMavenPublication
+
+plugins {
+    `java-platform`
+}
+
+val name = project.name
+
+dependencies {
+    constraints {
+        rootProject.subprojects.forEach {
+            if (it.name == name) return@forEach
+            if (!it.plugins.hasPlugin("maven-publish")) return@forEach
+            evaluationDependsOn(it.path)
+            it.publishing.publications.all {
+                this as MavenPublication
+                if (artifactId.endsWith("-kotlinMultiplatform")) return@all
+                if (artifactId.endsWith("-metadata")) return@all
+                // Skip platform artifacts (like *-linuxx64, *-macosx64)
+                // It leads to inconsistent bom when publishing from different platforms
+                // (e.g. on linux it will include only linuxx64 artifacts and no macosx64)
+                // It shouldn't be a problem as usually consumers need to use generic *-native artifact
+                // Gradle will choose correct variant by using metadata attributes
+                if (artifacts.any { it.extension == "klib" }) return@all
+                [email protected](mapOf("group" to groupId, "name" to artifactId, "version" to version))
+            }
+        }
+    }
+}
+
+publishing {
+    publications {
+        val mavenBom by creating(MavenPublication::class) {
+            from(components["javaPlatform"])
+        }
+        // Disable metadata publication
+        forEach { pub ->
+            pub as DefaultMavenPublication
+            pub.unsetModuleDescriptorGenerator()
+            tasks.matching { it.name == "generateMetadataFileFor${pub.name.capitalize()}Publication" }.all {
+                onlyIf { false }
+            }
+        }
+    }
+}
+
+fun DefaultMavenPublication.unsetModuleDescriptorGenerator() {
+    @Suppress("NULL_FOR_NONNULL_TYPE")
+    val generator: TaskProvider<Task?> = null
+    setModuleDescriptorGenerator(generator)
+}
diff --git a/build.gradle b/build.gradle
deleted file mode 100644
index 34765ef..0000000
--- a/build.gradle
+++ /dev/null
@@ -1,255 +0,0 @@
-/*
- * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-buildscript {
-    /**
-     * Overrides for Teamcity 'K2 User Projects' + 'Aggregate build / Kotlinx libraries compilation' configuration:
-     * kotlin_repo_url - local repository with snapshot Kotlin compiler
-     * kotlin_version - kotlin version to use
-     * kotlin_language_version - LV to use
-     */
-    ext.snapshotRepoUrl = rootProject.properties["kotlin_repo_url"]
-    ext.kotlin_lv_override = rootProject.properties["kotlin_language_version"]
-    if (snapshotRepoUrl != null && snapshotRepoUrl != "") {
-        ext.kotlin_version = rootProject.properties["kotlin_version"]
-        repositories {
-            maven { url snapshotRepoUrl }
-        }
-    } else if (project.hasProperty("bootstrap")) {
-        ext.kotlin_version = property('kotlin.version.snapshot')
-        ext["kotlin.native.home"] = System.getenv("KONAN_LOCAL_DIST")
-    } else {
-        ext.kotlin_version = property('kotlin.version')
-    }
-    if (project.hasProperty("library.version")) {
-        ext.overriden_version = property('library.version')
-    }
-    ext.experimentalsEnabled = ["-progressive",
-                                "-opt-in=kotlin.ExperimentalMultiplatform",
-                                "-opt-in=kotlinx.serialization.InternalSerializationApi",
-                                "-P", "plugin:org.jetbrains.kotlinx.serialization:disableIntrinsic=false"
-    ]
-
-    ext.experimentalsInTestEnabled = ["-progressive",
-                                      "-opt-in=kotlin.ExperimentalMultiplatform",
-                                      "-opt-in=kotlinx.serialization.ExperimentalSerializationApi",
-                                      "-opt-in=kotlinx.serialization.InternalSerializationApi",
-                                      "-P", "plugin:org.jetbrains.kotlinx.serialization:disableIntrinsic=false"
-    ]
-    ext.koverEnabled = property('kover.enabled') ?: true
-
-    def noTeamcityInteractionFlag = rootProject.hasProperty("no_teamcity_interaction")
-    def buildSnapshotUPFlag = rootProject.hasProperty("build_snapshot_up")
-    ext.teamcityInteractionDisabled = noTeamcityInteractionFlag || buildSnapshotUPFlag
-
-    /*
-    * This property group is used to build kotlinx.serialization against Kotlin compiler snapshot.
-    * When build_snapshot_train is set to true, kotlin_version property is overridden with kotlin_snapshot_version.
-    * DO NOT change the name of these properties without adapting kotlinx.train build chain.
-    */
-    def prop = rootProject.properties['build_snapshot_train']
-    ext.build_snapshot_train = prop != null && prop != ""
-    if (build_snapshot_train) {
-        ext.kotlin_version = rootProject.properties['kotlin_snapshot_version']
-        if (kotlin_version == null) {
-            throw new IllegalArgumentException("'kotlin_snapshot_version' should be defined when building with snapshot compiler")
-        }
-        repositories {
-            maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
-        }
-    }
-
-    repositories {
-        maven { url 'https://maven.pkg.jetbrains.space/kotlin/p/dokka/dev' }
-        // kotlin-dev with space redirector
-        maven { url "https://cache-redirector.jetbrains.com/maven.pkg.jetbrains.space/kotlin/p/kotlin/dev" }
-        mavenCentral()
-        gradlePluginPortal()
-        // For Dokka that depends on kotlinx-html
-        maven { url "https://maven.pkg.jetbrains.space/public/p/kotlinx-html/maven" }
-        mavenLocal()
-    }
-
-    configurations.classpath {
-        resolutionStrategy.eachDependency { DependencyResolveDetails details ->
-            if (details.requested.group == 'org.jetbrains.kotlin') {
-                details.useVersion kotlin_version
-            }
-        }
-    }
-
-    dependencies {
-        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
-        classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
-        classpath "org.jetbrains.dokka:dokka-gradle-plugin:$dokka_version"
-        classpath "org.jetbrains.kotlinx:kover:$kover_version"
-        classpath "org.jetbrains.kotlinx:binary-compatibility-validator:$validator_version"
-        classpath "org.jetbrains.kotlinx:kotlinx-knit:$knit_version"
-        classpath 'ru.vyarus:gradle-animalsniffer-plugin:1.5.3' // Android API check
-
-        classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.18'
-
-        // Various benchmarking stuff
-        classpath "com.github.jengelman.gradle.plugins:shadow:4.0.2"
-        classpath "me.champeau.jmh:jmh-gradle-plugin:0.6.6"
-    }
-}
-
-// To make it visible for compiler-version.gradle
-ext.compilerVersion = org.jetbrains.kotlin.config.KotlinCompilerVersion.VERSION
-ext.nativeDebugBuild = org.jetbrains.kotlin.gradle.plugin.mpp.NativeBuildType.DEBUG
-
-apply plugin: 'binary-compatibility-validator'
-apply plugin: 'base'
-apply plugin: 'kotlinx-knit'
-
-apiValidation {
-    ignoredProjects += ["benchmark", "guide", "kotlinx-serialization"]
-}
-
-knit {
-    siteRoot = "https://kotlinlang.org/api/kotlinx.serialization"
-    moduleDocs = "build/dokka/htmlMultiModule"
-}
-
-// Build API docs for all modules with dokka before running Knit
-knitPrepare.dependsOn "dokka"
-
-apply plugin: 'org.jetbrains.dokka'
-dependencies {
-    dokkaPlugin("org.jetbrains.kotlinx:dokka-pathsaver-plugin:$knit_version")
-}
-
-allprojects {
-    group 'org.jetbrains.kotlinx'
-
-    def deployVersion = properties['DeployVersion']
-    if (deployVersion != null) version = deployVersion
-
-    if (project.hasProperty("bootstrap")) {
-        version = version + '-SNAPSHOT'
-    }
-
-    // the only place where HostManager could be instantiated
-    project.ext.hostManager = new org.jetbrains.kotlin.konan.target.HostManager()
-
-    if (build_snapshot_train) {
-        // Snapshot-specific
-        repositories {
-            mavenLocal()
-            maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
-        }
-    }
-
-    if (snapshotRepoUrl != null && snapshotRepoUrl != "") {
-        // Snapshot-specific for K2 CI configurations
-        repositories {
-            maven { url snapshotRepoUrl }
-        }
-    }
-
-    configurations.all {
-        resolutionStrategy.eachDependency { DependencyResolveDetails details ->
-            if (details.requested.group == 'org.jetbrains.kotlin') {
-                details.useVersion kotlin_version
-            }
-        }
-    }
-
-    repositories {
-        mavenCentral()
-        maven { url 'https://maven.pkg.jetbrains.space/kotlin/p/dokka/dev' }
-        // kotlin-dev with space redirector
-        maven { url "https://cache-redirector.jetbrains.com/maven.pkg.jetbrains.space/kotlin/p/kotlin/dev" }
-        // For Dokka that depends on kotlinx-html
-        maven { url "https://maven.pkg.jetbrains.space/public/p/kotlinx-html/maven" }
-        // For local development
-        mavenLocal()
-
-    }
-
-    tasks.withType(org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile).configureEach {
-        compilerOptions { freeCompilerArgs.add("-Xpartial-linkage-loglevel=ERROR") }
-    }
-    tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile).configureEach {
-        compilerOptions { freeCompilerArgs.add("-Xpartial-linkage-loglevel=ERROR") }
-    }
-}
-
-def unpublishedProjects = ["benchmark", "guide", "kotlinx-serialization-json-tests"] as Set
-def excludedFromBomProjects = unpublishedProjects + "kotlinx-serialization-bom" as Set
-def uncoveredProjects = ["kotlinx-serialization-bom", "benchmark", "guide"] as Set
-
-subprojects {
-    tasks.withType(org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile).all { task ->
-        if (task.name.contains("Test") || task.name.contains("Jmh")) {
-            task.kotlinOptions.freeCompilerArgs += experimentalsInTestEnabled
-        } else {
-            task.kotlinOptions.freeCompilerArgs += experimentalsEnabled
-        }
-    }
-
-    apply from: rootProject.file('gradle/teamcity.gradle')
-    // Configure publishing for some artifacts
-    if (!unpublishedProjects.contains(project.name)) {
-        apply from: rootProject.file('gradle/publishing.gradle')
-    }
-}
-
-subprojects {
-    // Can't be applied to BOM
-    if (excludedFromBomProjects.contains(project.name)) return
-
-    // Animalsniffer setup
-    // Animalsniffer requires java plugin to be applied, but Kotlin 1.9.20
-    // relies on `java-base` for Kotlin Multiplatforms `withJava` implementation
-    // https://github.com/xvik/gradle-animalsniffer-plugin/issues/84
-    // https://youtrack.jetbrains.com/issue/KT-59595
-    JavaPluginUtil.applyJavaPlugin(project)
-    apply plugin: 'ru.vyarus.animalsniffer'
-
-    afterEvaluate { // Can be applied only when the project is evaluated
-        animalsniffer {
-            sourceSets = [sourceSets.main]
-            def annotationValue = "kotlinx.serialization.json.internal.SuppressAnimalSniffer"
-            switch (name) {
-                case "kotlinx-serialization-core":
-                    annotationValue = "kotlinx.serialization.internal.SuppressAnimalSniffer"
-                    break
-                case "kotlinx-serialization-hocon":
-                    annotationValue = "kotlinx.serialization.hocon.internal.SuppressAnimalSniffer"
-                    break
-                case "kotlinx-serialization-protobuf":
-                    annotationValue = "kotlinx.serialization.protobuf.internal.SuppressAnimalSniffer"
-            }
-            annotation = annotationValue
-        }
-        dependencies {
-            signature 'net.sf.androidscents.signature:android-api-level-14:4.0_r4@signature'
-            signature 'org.codehaus.mojo.signature:java18:1.0@signature'
-        }
-
-        // Add dependency on kotlinx-serialization-bom inside other kotlinx-serialization modules themselves, so they have same versions
-        BomKt.addBomApiDependency(project, ":kotlinx-serialization-bom")
-    }
-}
-
-// Kover setup
-subprojects {
-    if (uncoveredProjects.contains(project.name)) return
-
-    apply from: rootProject.file("gradle/kover.gradle")
-}
-
-apply from: rootProject.file('gradle/compiler-version.gradle')
-apply from: rootProject.file("gradle/dokka.gradle")
-apply from: rootProject.file("gradle/benchmark-parsing.gradle")
-
-tasks.named("dokkaHtmlMultiModule") {
-    pluginsMapConfiguration.set(["org.jetbrains.dokka.base.DokkaBase": """{ "templatesDir": "${projectDir.toString().replace('\\', '/')}/dokka-templates" }"""])
-}
-
-tasks.withType(org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinNpmInstallTask).configureEach {
-    args.add("--ignore-engines")
-}
\ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts
new file mode 100644
index 0000000..8d71233
--- /dev/null
+++ b/build.gradle.kts
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+import kotlinx.validation.*
+import org.jetbrains.dokka.gradle.*
+
+plugins {
+    base
+    alias(libs.plugins.knit)
+    id("org.jetbrains.kotlinx.binary-compatibility-validator")
+    id("org.jetbrains.dokka")
+    id("benchmark-conventions")
+    id("publishing-check-conventions")
+    id("kover-conventions")
+
+    alias(libs.plugins.serialization) apply false
+}
+
+repositories {
+    mavenCentral()
+    maven("https://maven.pkg.jetbrains.space/kotlin/p/dokka/dev")
+    // kotlin-dev with space redirector
+    maven("https://cache-redirector.jetbrains.com/maven.pkg.jetbrains.space/kotlin/p/kotlin/dev")
+    // For Dokka that depends on kotlinx-html
+    maven("https://maven.pkg.jetbrains.space/public/p/kotlinx-html/maven")
+    // For local development
+    mavenLocal()
+}
+
+// == common projects settings setup
+allprojects {
+    // group setup
+    group = "org.jetbrains.kotlinx"
+
+    // version setup
+    val deployVersion = properties["DeployVersion"]
+    if (deployVersion != null) version = deployVersion
+    if (project.hasProperty("bootstrap")) {
+        version = "$version-SNAPSHOT"
+    }
+
+    // repositories setup
+    if (propertyIsTrue("build_snapshot_train")) {
+        // Snapshot-specific
+        repositories {
+            mavenLocal()
+            maven("https://oss.sonatype.org/content/repositories/snapshots")
+        }
+    }
+    val snapshotRepoUrl = findProperty("kotlin_repo_url")
+    if (snapshotRepoUrl != null && snapshotRepoUrl != "") {
+        // Snapshot-specific for K2 CI configurations
+        repositories {
+            maven(snapshotRepoUrl)
+        }
+    }
+    repositories {
+        mavenCentral()
+        maven("https://cache-redirector.jetbrains.com/maven.pkg.jetbrains.space/kotlin/p/kotlin/dev")
+    }
+}
+
+// == BCV setup ==
+apiValidation {
+    ignoredProjects.addAll(listOf("benchmark", "guide", "kotlinx-serialization", "kotlinx-serialization-json-tests"))
+    @OptIn(ExperimentalBCVApi::class)
+    klib {
+        enabled = true
+    }
+}
+
+// == Knit setup ==
+
+knit {
+    siteRoot = "https://kotlinlang.org/api/kotlinx.serialization"
+    moduleDocs = "build/dokka/htmlMultiModule"
+}
+
+// Build API docs for all modules with dokka before running Knit
+tasks.named("knitPrepare") {
+    dependsOn("dokka")
+}
+
+
+// == compiler flags setup ==
+
+tasks.withType<org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile>().configureEach {
+    compilerOptions { freeCompilerArgs.add("-Xpartial-linkage-loglevel=ERROR") }
+}
+tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinNativeCompile>().configureEach {
+    compilerOptions { freeCompilerArgs.add("-Xpartial-linkage-loglevel=ERROR") }
+}
+
+subprojects {
+    tasks.withType<org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile<*>>().configureEach {
+        compilerOptions.freeCompilerArgs.addAll(globalCompilerArgs)
+    }
+}
+
+// == TeamCity setup ==
+subprojects {
+    apply(plugin = "teamcity-conventions")
+}
+
+// == publishing setup ==
+subprojects {
+    if (name in unpublishedProjects) return@subprojects
+    apply(plugin = "publishing-conventions")
+}
+
+// == publishing setup ==
+
+val mergeProject = project
+
+subprojects {
+    if (name in unpublishedProjects) return@subprojects
+    apply(plugin = "publishing-conventions")
+    mergeProject.dependencies.add(Publishing_check_conventions_gradle.TestPublishing.configurationName, this)
+}
+
+// == animalsniffer setup ==
+subprojects {
+    // Can't be applied to BOM
+    if (project.name in excludedFromBomProjects) return@subprojects
+    apply(plugin = "animalsniffer-conventions")
+}
+
+// == BOM setup ==
+subprojects {
+    // Can't be applied to BOM
+    if (project.name in excludedFromBomProjects) return@subprojects
+    apply(plugin = "bom-conventions")
+}
+
+// == Dokka setup ==
+subprojects {
+    if (name in documentedSubprojects) {
+        apply(plugin = "dokka-conventions")
+    }
+}
+
+// Knit relies on Dokka task and it's pretty convenient
+tasks.register("dokka") {
+    dependsOn("dokkaHtmlMultiModule")
+}
+
+tasks.withType<DokkaMultiModuleTask>().named("dokkaHtmlMultiModule") {
+    pluginsMapConfiguration.put("org.jetbrains.dokka.base.DokkaBase", """{ "templatesDir": "${projectDir.toString().replace("\\", "/")}/dokka-templates" }""")
+}
+
+dependencies {
+    dokkaPlugin(libs.dokka.pathsaver)
+}
+
+// == NPM setup ==
+
+tasks.withType<org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinNpmInstallTask>().configureEach {
+    args.add("--ignore-engines")
+}
+
+// == compiler version setup ==
+gradle.taskGraph.whenReady {
+    println("Using Kotlin compiler version: ${org.jetbrains.kotlin.config.KotlinCompilerVersion.VERSION}")
+}
+
+// == projects lists and flags ==
+// getters are required because of variable lazy initialization in Gradle
+val unpublishedProjects get() = setOf("benchmark", "guide", "kotlinx-serialization-json-tests")
+val excludedFromBomProjects get() = unpublishedProjects + "kotlinx-serialization-bom"
+val globalCompilerArgs
+    get() = listOf(
+    "-P", "plugin:org.jetbrains.kotlinx.serialization:disableIntrinsic=false"
+)
+
+val documentedSubprojects get() = setOf("kotlinx-serialization-core",
+    "kotlinx-serialization-json",
+    "kotlinx-serialization-json-okio",
+    "kotlinx-serialization-json-io",
+    "kotlinx-serialization-cbor",
+    "kotlinx-serialization-properties",
+    "kotlinx-serialization-hocon",
+    "kotlinx-serialization-protobuf")
diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts
index c999bcd..295378b 100644
--- a/buildSrc/build.gradle.kts
+++ b/buildSrc/build.gradle.kts
@@ -2,43 +2,46 @@
  * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
  */
 
-import java.util.*
-import java.io.FileInputStream
-
 plugins {
     `kotlin-dsl`
 }
 
 repositories {
-    mavenCentral()
-    mavenLocal()
-    if (project.hasProperty("kotlin_repo_url")) {
-        maven(project.properties["kotlin_repo_url"] as String)
+    /**
+     * Overrides for Teamcity 'K2 User Projects' + 'Aggregate build / Kotlinx libraries compilation' configuration:
+     * kotlin_repo_url - local repository with snapshot Kotlin compiler
+     * kotlin_version - kotlin version to use
+     * kotlin_language_version - LV to use
+     */
+    val snapshotRepoUrl = findProperty("kotlin_repo_url") as String?
+    if (snapshotRepoUrl?.isNotEmpty() == true) {
+        maven(snapshotRepoUrl)
     }
+    /*
+    * This property group is used to build kotlinx.serialization against Kotlin compiler snapshot.
+    * When build_snapshot_train is set to true, kotlin_version property is overridden with kotlin_snapshot_version.
+    * DO NOT change the name of these properties without adapting kotlinx.train build chain.
+    */
+    if ((findProperty("build_snapshot_train") as? String?).equals("true", true)) {
+        maven("https://oss.sonatype.org/content/repositories/snapshots")
+    }
+
     // kotlin-dev with space redirector
     maven("https://cache-redirector.jetbrains.com/maven.pkg.jetbrains.space/kotlin/p/kotlin/dev")
-}
 
-val kotlinVersion = run {
-    if (project.hasProperty("build_snapshot_train")) {
-        val ver = project.properties["kotlin_snapshot_version"] as? String
-        require(!ver.isNullOrBlank()) {"kotlin_snapshot_version must be present if build_snapshot_train is used" }
-        return@run ver
-    }
-    if (project.hasProperty("kotlin_repo_url")) {
-        val ver = project.properties["kotlin_version"] as? String
-        require(!ver.isNullOrBlank()) {"kotlin_version must be present if kotlin_repo_url is used" }
-        return@run ver
-    }
-    val targetProp = if (project.hasProperty("bootstrap")) "kotlin.version.snapshot" else "kotlin.version"
-    FileInputStream(file("../gradle.properties")).use { propFile ->
-        val ver = project.findProperty("kotlin.version")?.toString() ?: Properties().apply { load(propFile) }[targetProp]
-        require(ver is String) { "$targetProp must be string in ../gradle.properties, got $ver instead" }
-        ver
-    }
+    maven("https://maven.pkg.jetbrains.space/kotlin/p/dokka/dev")
+    // For Dokka that depends on kotlinx-html
+    maven("https://maven.pkg.jetbrains.space/public/p/kotlinx-html/maven")
+
+    mavenCentral()
+    mavenLocal()
 }
 
 dependencies {
-    implementation(kotlin("gradle-plugin", kotlinVersion))
+    implementation(libs.gradlePlugin.kotlin)
+    implementation(libs.gradlePlugin.kover)
+    implementation(libs.gradlePlugin.dokka)
+    implementation(libs.gradlePlugin.animalsniffer)
+    implementation(libs.gradlePlugin.binaryCompatibilityValidator)
 }
 
diff --git a/buildSrc/settings.gradle.kts b/buildSrc/settings.gradle.kts
new file mode 100644
index 0000000..b450fd5
--- /dev/null
+++ b/buildSrc/settings.gradle.kts
@@ -0,0 +1,51 @@
+import java.io.*
+import java.util.*
+
+/*
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+dependencyResolutionManagement {
+    versionCatalogs {
+        create("libs") {
+            from(files("../gradle/libs.versions.toml"))
+
+            overriddenKotlinVersion()?.also { overriddenVersion ->
+                logger.info("Overriding Kotlin version in buildSrc: $overriddenVersion")
+                version("kotlin", overriddenVersion)
+            }
+        }
+    }
+}
+
+fun overriddenKotlinVersion(): String? {
+    val kotlinRepoUrl: String? = providers.gradleProperty("kotlin_repo_url").orNull
+    val repoVersion: String? = providers.gradleProperty("kotlin_version").orNull
+    val repoVersionFile: String?
+
+    val bootstrap: String? = providers.gradleProperty("bootstrap").orNull
+    val bootstrapVersion: String? = providers.gradleProperty("kotlin.version.snapshot").orNull
+    val bootstrapVersionFile: String?
+
+    val buildSnapshotTrain: String? = providers.gradleProperty("build_snapshot_train").orNull
+    val trainVersion: String? = providers.gradleProperty("kotlin_snapshot_version").orNull
+    val trainVersionFile: String?
+
+    FileInputStream(file("../gradle.properties")).use { propFile ->
+        val properties = Properties()
+        properties.load(propFile)
+        repoVersionFile = properties["kotlin_version"] as String?
+        bootstrapVersionFile = properties["kotlin.version.snapshot"] as String?
+        trainVersionFile = properties["kotlin_snapshot_version"] as String?
+    }
+
+    if (kotlinRepoUrl?.isNotEmpty() == true) {
+        return repoVersion ?: repoVersionFile ?: throw IllegalArgumentException("\"kotlin_version\" Gradle property should be defined")
+    } else if (bootstrap != null) {
+        return bootstrapVersion ?: bootstrapVersionFile ?: throw IllegalArgumentException("\"kotlin.version.snapshot\" Gradle property should be defined")
+    }
+    if (buildSnapshotTrain?.isNotEmpty() == true) {
+        return trainVersion ?: trainVersionFile ?: throw IllegalArgumentException("\"kotlin_snapshot_version\" should be defined when building with snapshot compiler")
+    }
+    return null
+}
\ No newline at end of file
diff --git a/buildSrc/src/main/kotlin/Java9Modularity.kt b/buildSrc/src/main/kotlin/Java9Modularity.kt
index 2743b00..313c9ce 100644
--- a/buildSrc/src/main/kotlin/Java9Modularity.kt
+++ b/buildSrc/src/main/kotlin/Java9Modularity.kt
@@ -21,6 +21,7 @@
 import org.jetbrains.kotlin.gradle.tasks.*
 import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
 import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
+import org.jetbrains.kotlin.gradle.utils.*
 import org.jetbrains.kotlin.tooling.core.*
 import java.io.*
 import kotlin.reflect.*
@@ -113,6 +114,7 @@
         sourceFile: File
     ): TaskProvider<out KotlinJvmCompile> {
         apply<KotlinApiPlugin>()
+        @Suppress("DEPRECATION")
         val verifyModuleTaskName = "verify${compileTask.name.removePrefix("compile").capitalize()}Module"
         // work-around for https://youtrack.jetbrains.com/issue/KT-60542
         val kotlinApiPlugin = plugins.getPlugin(KotlinApiPlugin::class)
@@ -139,7 +141,7 @@
                 freeCompilerArgs.addAll(
                     listOf("-Xjdk-release=9",  "-Xsuppress-version-warnings", "-Xexpect-actual-classes")
                 )
-                optIn.addAll(compileTask.kotlinOptions.options.optIn)
+                optIn.addAll(compileTask.compilerOptions.optIn)
             }
             // work-around for https://youtrack.jetbrains.com/issue/KT-60583
             inputs.files(
@@ -160,7 +162,7 @@
                     .declaredMemberProperties
                     .find { it.name == "ownModuleName" }
                     ?.get(this) as? Property<String>
-                ownModuleNameProp?.set(compileTask.kotlinOptions.moduleName)
+                ownModuleNameProp?.set(compileTask.compilerOptions.moduleName)
             }
 
             val taskKotlinLanguageVersion = compilerOptions.languageVersion.orElse(KotlinVersion.DEFAULT)
diff --git a/buildSrc/src/main/kotlin/KotlinVersion.kt b/buildSrc/src/main/kotlin/KotlinVersion.kt
deleted file mode 100644
index 5ac051e..0000000
--- a/buildSrc/src/main/kotlin/KotlinVersion.kt
+++ /dev/null
@@ -1,14 +0,0 @@
-@file:JvmName("KotlinVersion")
-
-fun isKotlinVersionAtLeast(kotlinVersion: String, atLeastMajor: Int, atLeastMinor: Int, atLeastPatch: Int): Boolean {
-    val (major, minor) = kotlinVersion
-        .split('.')
-        .take(2)
-        .map { it.toInt() }
-    val patch = kotlinVersion.substringAfterLast('.').substringBefore('-').toInt()
-    return when {
-        major > atLeastMajor -> true
-        major < atLeastMajor -> false
-        else -> (minor == atLeastMinor && patch >= atLeastPatch) || minor > atLeastMinor
-    }
-}
diff --git a/buildSrc/src/main/kotlin/Projects.kt b/buildSrc/src/main/kotlin/Projects.kt
new file mode 100644
index 0000000..c74abc1
--- /dev/null
+++ b/buildSrc/src/main/kotlin/Projects.kt
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+import org.gradle.api.*
+import org.gradle.api.tasks.*
+
+val Project.sourceSets: SourceSetContainer
+    get() = extensions.getByName("sourceSets") as SourceSetContainer
+
+fun Project.propertyIsTrue(propertyName: String): Boolean {
+    return (findProperty(propertyName) as? String?).equals("true", true)
+}
+
+val Project.jdkToolchainVersion: Int get() = findProperty("jdk_toolchain_version").toString().toInt()
+
+val Project.overriddenLanguageVersion : String?
+    get() = findProperty("kotlin_language_version") as String?
+
+val Project.teamcityInteractionEnabled : Boolean
+    get() = !hasProperty("no_teamcity_interaction") && !hasProperty("build_snapshot_up")
diff --git a/buildSrc/src/main/kotlin/Publishing.kt b/buildSrc/src/main/kotlin/Publishing.kt
deleted file mode 100644
index a855da9..0000000
--- a/buildSrc/src/main/kotlin/Publishing.kt
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-@file:Suppress("UnstableApiUsage")
-
-import org.gradle.api.*
-import org.gradle.api.artifacts.dsl.*
-import org.gradle.api.provider.*
-import org.gradle.api.publish.maven.*
-import org.gradle.plugins.signing.*
-import java.net.*
-
-// Pom configuration
-
-infix fun <T> Property<T>.by(value: T) {
-    set(value)
-}
-
-fun MavenPom.configureMavenCentralMetadata(project: Project) {
-    name by project.name
-    description by "Kotlin multiplatform serialization runtime library"
-    url by "https://github.com/Kotlin/kotlinx.serialization"
-
-    licenses {
-        license {
-            name by "The Apache Software License, Version 2.0"
-            url by "https://www.apache.org/licenses/LICENSE-2.0.txt"
-            distribution by "repo"
-        }
-    }
-
-    developers {
-        developer {
-            id by "JetBrains"
-            name by "JetBrains Team"
-            organization by "JetBrains"
-            organizationUrl by "https://www.jetbrains.com"
-        }
-    }
-
-    scm {
-        url by "https://github.com/Kotlin/kotlinx.serialization"
-    }
-}
-
-fun mavenRepositoryUri(): URI {
-    // TODO -SNAPSHOT detection can be made here as well
-    val repositoryId: String? = System.getenv("libs.repository.id")
-    return if (repositoryId == null) {
-        URI("https://oss.sonatype.org/service/local/staging/deploy/maven2/")
-    } else {
-        URI("https://oss.sonatype.org/service/local/staging/deployByRepositoryId/$repositoryId")
-    }
-}
-
-fun configureMavenPublication(rh: RepositoryHandler, project: Project) {
-    rh.maven {
-        url = mavenRepositoryUri()
-        credentials {
-            username = project.getSensitiveProperty("libs.sonatype.user")
-            password = project.getSensitiveProperty("libs.sonatype.password")
-        }
-    }
-}
-
-fun signPublicationIfKeyPresent(project: Project, publication: MavenPublication) {
-    val keyId = project.getSensitiveProperty("libs.sign.key.id")
-    val signingKey = project.getSensitiveProperty("libs.sign.key.private")
-    val signingKeyPassphrase = project.getSensitiveProperty("libs.sign.passphrase")
-    if (!signingKey.isNullOrBlank()) {
-        project.extensions.configure<SigningExtension>("signing") {
-            useInMemoryPgpKeys(keyId, signingKey, signingKeyPassphrase)
-            sign(publication)
-        }
-    }
-}
-
-private fun Project.getSensitiveProperty(name: String): String? {
-    return project.findProperty(name) as? String ?: System.getenv(name)
-}
diff --git a/buildSrc/src/main/kotlin/animalsniffer-conventions.gradle.kts b/buildSrc/src/main/kotlin/animalsniffer-conventions.gradle.kts
new file mode 100644
index 0000000..f280589
--- /dev/null
+++ b/buildSrc/src/main/kotlin/animalsniffer-conventions.gradle.kts
@@ -0,0 +1,67 @@
+import org.gradle.api.*
+import org.gradle.api.plugins.*
+import org.gradle.api.tasks.*
+import org.gradle.api.tasks.bundling.*
+import org.gradle.api.tasks.testing.*
+import org.gradle.kotlin.dsl.*
+import ru.vyarus.gradle.plugin.animalsniffer.AnimalSnifferExtension
+
+/*
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+// Animalsniffer setup
+// Animalsniffer requires java plugin to be applied, but Kotlin 1.9.20
+// relies on `java-base` for Kotlin Multiplatforms `withJava` implementation
+// https://github.com/xvik/gradle-animalsniffer-plugin/issues/84
+// https://youtrack.jetbrains.com/issue/KT-59595
+plugins {
+    java
+    id("ru.vyarus.animalsniffer")
+}
+
+
+plugins.withId("org.jetbrains.kotlin.multiplatform") {
+    listOf(
+        JavaPlugin.API_ELEMENTS_CONFIGURATION_NAME,
+        JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME
+    ).forEach { outputConfigurationName ->
+        configurations.findByName(outputConfigurationName)?.isCanBeConsumed = false
+    }
+
+    disableJavaPluginTasks(extensions.getByName("sourceSets") as SourceSetContainer)
+}
+
+fun Project.disableJavaPluginTasks(javaSourceSet: SourceSetContainer) {
+    project.tasks.withType(Jar::class.java).named(javaSourceSet.getByName("main").jarTaskName).configure {
+        dependsOn("jvmTest")
+        enabled = false
+    }
+
+    project.tasks.withType(Test::class.java).named(JavaPlugin.TEST_TASK_NAME) {
+        dependsOn("jvmJar")
+        enabled = false
+    }
+}
+
+
+afterEvaluate { // Can be applied only when the project is evaluated
+    extensions.configure<AnimalSnifferExtension> {
+        sourceSets = listOf([email protected]["main"])
+
+        val annotationValue = when(name) {
+            "kotlinx-serialization-core" -> "kotlinx.serialization.internal.SuppressAnimalSniffer"
+            "kotlinx-serialization-hocon" -> "kotlinx.serialization.hocon.internal.SuppressAnimalSniffer"
+            "kotlinx-serialization-protobuf" -> "kotlinx.serialization.protobuf.internal.SuppressAnimalSniffer"
+            "kotlinx-serialization-cbor" -> "kotlinx.serialization.cbor.internal.SuppressAnimalSniffer"
+            else -> "kotlinx.serialization.json.internal.SuppressAnimalSniffer"
+        }
+
+        annotation = annotationValue
+    }
+    dependencies {
+        "signature"("net.sf.androidscents.signature:android-api-level-14:4.0_r4@signature")
+        "signature"("org.codehaus.mojo.signature:java18:1.0@signature")
+    }
+
+}
\ No newline at end of file
diff --git a/buildSrc/src/main/kotlin/benchmark-conventions.gradle.kts b/buildSrc/src/main/kotlin/benchmark-conventions.gradle.kts
new file mode 100644
index 0000000..b70b577
--- /dev/null
+++ b/buildSrc/src/main/kotlin/benchmark-conventions.gradle.kts
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+import groovy.json.JsonSlurper
+import java.io.FileNotFoundException
+
+/**
+ * Utility for printing benchmark results.
+ * Results can be obtained with JMH flags
+ * -rf json -rff serialization-benchmark-results.json
+ */
+open class PrintBenchmarksTask: DefaultTask() {
+    private val fileName: String = "serialization-benchmark-results.json"
+
+    @Suppress("UNCHECKED_CAST")
+    @TaskAction
+    fun printBenchmarkJsonAsTeamcityStats() {
+        val jsonFile = project.file(fileName)
+        if (!jsonFile.exists()) throw TaskExecutionException(this, FileNotFoundException("File $fileName not found"))
+        val parsedJson = JsonSlurper().parseText(jsonFile.readText()) as Iterable<Map<String, Any>>
+
+        parsedJson.forEach { v ->
+            val name = (v["benchmark"] as String).substringAfter("kotlinx.benchmarks.")
+            val score = (v["primaryMetric"] as Map<String, String>)["score"]
+            println("##teamcity[buildStatisticValue key='$name' value='$score']")
+        }
+    }
+}
+
+tasks.register<PrintBenchmarksTask>("printBenchmarksJsonAsTeamcityStats")
diff --git a/buildSrc/src/main/kotlin/Bom.kt b/buildSrc/src/main/kotlin/bom-conventions.gradle.kts
similarity index 75%
rename from buildSrc/src/main/kotlin/Bom.kt
rename to buildSrc/src/main/kotlin/bom-conventions.gradle.kts
index 7f93ed3..12df5e3 100644
--- a/buildSrc/src/main/kotlin/Bom.kt
+++ b/buildSrc/src/main/kotlin/bom-conventions.gradle.kts
@@ -1,12 +1,11 @@
 /*
- * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
  */
 
-import org.gradle.api.Project
 import org.gradle.kotlin.dsl.*
 import org.jetbrains.kotlin.gradle.dsl.*
 
-fun Project.addBomApiDependency(bomProjectPath: String) {
+afterEvaluate {
     val isMultiplatform = plugins.hasPlugin("kotlin-multiplatform")
 
     if (isMultiplatform) {
@@ -20,3 +19,4 @@
     }
 }
 
+val bomProjectPath = ":kotlinx-serialization-bom"
diff --git a/buildSrc/src/main/kotlin/dokka-conventions.gradle.kts b/buildSrc/src/main/kotlin/dokka-conventions.gradle.kts
new file mode 100644
index 0000000..3a783f7
--- /dev/null
+++ b/buildSrc/src/main/kotlin/dokka-conventions.gradle.kts
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+import org.jetbrains.dokka.gradle.*
+import java.net.URL
+
+/*
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+plugins {
+    id("org.jetbrains.dokka")
+}
+
+val extens = extensions
+dependencies {
+    dokkaPlugin(provider { extens.getByType<VersionCatalogsExtension>().named("libs").findLibrary("dokka.pathsaver").get().get() })
+}
+
+tasks.withType<DokkaTaskPartial>().named("dokkaHtmlPartial") {
+    outputDirectory.set(file("build/dokka"))
+
+
+    pluginsMapConfiguration.put("org.jetbrains.dokka.base.DokkaBase", """{ "templatesDir": "${rootDir.resolve("dokka-templates").canonicalPath.replace('\\', '/')}" }""")
+
+    dokkaSourceSets {
+        configureEach {
+            includes.from(rootDir.resolve("dokka/moduledoc.md").path)
+
+            perPackageOption {
+                matchingRegex.set("kotlinx\\.serialization(\$|\\.).*")
+                reportUndocumented.set(true)
+                skipDeprecated.set(true)
+            }
+
+            // Internal API
+            perPackageOption {
+                matchingRegex.set("kotlinx\\.serialization.internal(\$|\\.).*")
+                suppress.set(true)
+            }
+
+            // Internal JSON API
+            perPackageOption {
+                matchingRegex.set("kotlinx\\.serialization.json.internal(\$|\\.).*")
+                suppress.set(true)
+                reportUndocumented.set(false)
+            }
+
+            // Workaround for typealias
+            perPackageOption {
+                matchingRegex.set("kotlinx\\.serialization.protobuf.internal(\$|\\.).*")
+                suppress.set(true)
+                reportUndocumented.set(false)
+            }
+
+            // Deprecated migrations
+            perPackageOption {
+                matchingRegex.set("kotlinx\\.protobuf(\$|\\.).*")
+                reportUndocumented.set(true)
+                skipDeprecated.set(true)
+            }
+
+            // Deprecated migrations
+            perPackageOption {
+                matchingRegex.set("org\\.jetbrains\\.kotlinx\\.serialization\\.config(\$|\\.).*")
+                reportUndocumented.set(false)
+                skipDeprecated.set(true)
+            }
+
+            sourceLink {
+                localDirectory.set(rootDir)
+
+                remoteUrl.set(URL("https://github.com/Kotlin/kotlinx.serialization/tree/master"))
+                remoteLineSuffix.set("#L")
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/buildSrc/src/main/kotlin/kover-conventions.gradle.kts b/buildSrc/src/main/kotlin/kover-conventions.gradle.kts
new file mode 100644
index 0000000..cc64d45
--- /dev/null
+++ b/buildSrc/src/main/kotlin/kover-conventions.gradle.kts
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+plugins {
+    id("org.jetbrains.kotlinx.kover")
+}
+
+kover {
+    if (hasProperty("kover.enabled") && property("kover.enabled") != "true") {
+        disable()
+    }
+
+    currentProject {
+        projectsForCoverageVerification.forEach { (variantName, _) ->
+            // copy the `main` variant for each module to check the coverage only in its section
+            copyVariant(variantName, "main")
+        }
+    }
+
+    merge {
+        // collect common coverage for all projects (except excluded) in `main` variant
+        subprojects { subproject ->
+            subproject.path !in uncoveredProjects
+        }
+        createVariant("main") { add("jvm", optional = true) }
+    }
+
+    reports {
+        total.verify.rule("Total coverage") {
+            minBound(90)
+        }
+
+        projectsForCoverageVerification.forEach { (variantName, projectPath) ->
+            variant(variantName) {
+                filters.includes.projects.add(projectPath)
+
+                // verify the coverage individually for each module by `check` task
+                verify {
+                    onCheck = true
+                    rule("Coverage for $projectPath") {
+                        minBound(85)
+                    }
+                }
+            }
+        }
+    }
+}
+
+
+val uncoveredProjects get() = setOf(":kotlinx-serialization-bom", ":benchmark", ":guide")
+// map: variant name -> project path
+val projectsForCoverageVerification get() = mapOf("core" to ":kotlinx-serialization-core", "json" to ":kotlinx-serialization-json", "jsonOkio" to ":kotlinx-serialization-json-okio", "cbor" to ":kotlinx-serialization-cbor", "hocon" to ":kotlinx-serialization-hocon", "properties" to ":kotlinx-serialization-properties", "protobuf" to ":kotlinx-serialization-protobuf", "io" to ":kotlinx-serialization-json-io")
diff --git a/buildSrc/src/main/kotlin/native-targets-conventions.gradle.kts b/buildSrc/src/main/kotlin/native-targets-conventions.gradle.kts
new file mode 100644
index 0000000..d5cf624
--- /dev/null
+++ b/buildSrc/src/main/kotlin/native-targets-conventions.gradle.kts
@@ -0,0 +1,64 @@
+import org.jetbrains.kotlin.gradle.*
+import org.jetbrains.kotlin.gradle.plugin.mpp.*
+
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+plugins {
+    kotlin("multiplatform")
+}
+
+kotlin {
+    @OptIn(ExperimentalKotlinGradlePluginApi::class)
+    applyDefaultHierarchyTemplate {
+
+    }
+
+    // According to https://kotlinlang.org/docs/native-target-support.html
+    // Tier 1
+    macosX64()
+    macosArm64()
+    iosSimulatorArm64()
+    iosX64()
+
+    // Tier 2
+    linuxX64()
+    linuxArm64()
+    watchosSimulatorArm64()
+    watchosX64()
+    watchosArm32()
+    watchosArm64()
+    tvosSimulatorArm64()
+    tvosX64()
+    tvosArm64()
+    iosArm64()
+
+    // Tier 3
+    mingwX64()
+    // https://github.com/square/okio/issues/1242#issuecomment-1759357336
+    if (doesNotDependOnOkio(project)) {
+        androidNativeArm32()
+        androidNativeArm64()
+        androidNativeX86()
+        androidNativeX64()
+        watchosDeviceArm64()
+
+        // Deprecated, but not removed
+        linuxArm32Hfp()
+    }
+
+    // setup tests running in RELEASE mode
+    targets.withType<KotlinNativeTarget>().configureEach {
+        binaries.test(listOf(NativeBuildType.RELEASE))
+    }
+    targets.withType<KotlinNativeTargetWithTests<*>>().configureEach {
+        testRuns.create("releaseTest") {
+            setExecutionSourceFrom(binaries.getTest(NativeBuildType.RELEASE))
+        }
+    }
+}
+
+fun doesNotDependOnOkio(project: Project): Boolean {
+    return !project.name.contains("json-okio") && !project.name.contains("json-tests")
+}
\ No newline at end of file
diff --git a/buildSrc/src/main/kotlin/publishing-check-conventions.gradle.kts b/buildSrc/src/main/kotlin/publishing-check-conventions.gradle.kts
new file mode 100644
index 0000000..04021a4
--- /dev/null
+++ b/buildSrc/src/main/kotlin/publishing-check-conventions.gradle.kts
@@ -0,0 +1,65 @@
+object TestPublishing {
+    const val configurationName = "testRepository"
+}
+
+val testRepositoryDependency = configurations.create(TestPublishing.configurationName) {
+    isVisible = true
+    isCanBeResolved = false
+    isCanBeConsumed = false
+}
+
+
+val testRepositories = configurations.create("testRepositories") {
+    isVisible = false
+    isCanBeResolved = true
+    // this config consumes modules from OTHER projects, and cannot be consumed by other projects
+    isCanBeConsumed = false
+
+    attributes {
+        attribute(Attribute.of("kotlinx.serialization.repository", String::class.java), "test")
+    }
+    extendsFrom(testRepositoryDependency)
+}
+
+tasks.register<ArtifactsCheckTask>("checkArtifacts") {
+    repositories.from(testRepositories)
+}
+
+abstract class ArtifactsCheckTask: DefaultTask() {
+
+    @get:InputFiles
+    @get:PathSensitive(PathSensitivity.RELATIVE)
+    abstract val repositories: ConfigurableFileCollection
+
+    @TaskAction
+    fun check() {
+        val artifactsFile = project.rootDir.resolve("gradle/artifacts.txt")
+
+        val actualArtifacts = repositories.files.flatMap { file ->
+            file.resolve("org/jetbrains/kotlinx").list()?.toSet() ?: emptySet()
+        }.toSortedSet()
+
+        if (project.hasProperty("dumpArtifacts")) {
+            artifactsFile.bufferedWriter().use { writer ->
+                actualArtifacts.forEach { artifact -> writer.appendLine(artifact) }
+            }
+            return
+        }
+
+        val expectedArtifacts = artifactsFile.readLines().toSet()
+
+        if (expectedArtifacts == actualArtifacts) {
+            logger.lifecycle("All artifacts are published")
+        } else {
+            val missedArtifacts = expectedArtifacts - actualArtifacts
+            val unknownArtifacts = actualArtifacts - expectedArtifacts
+            val message = "The published artifacts differ from the expected ones." +
+                (if (missedArtifacts.isNotEmpty()) missedArtifacts.joinToString(prefix = "\n\tMissing artifacts: ") else "") +
+                (if (unknownArtifacts.isNotEmpty()) unknownArtifacts.joinToString(prefix = "\n\tUnknown artifacts: ") else "") +
+                "\nTo save current list of artifacts as expecting, call 'checkArtifacts -PdumpArtifacts'"
+
+            logger.error(message)
+            throw GradleException("The published artifacts differ from the expected ones")
+        }
+    }
+}
diff --git a/buildSrc/src/main/kotlin/publishing-conventions.gradle.kts b/buildSrc/src/main/kotlin/publishing-conventions.gradle.kts
new file mode 100644
index 0000000..6d0d3bf
--- /dev/null
+++ b/buildSrc/src/main/kotlin/publishing-conventions.gradle.kts
@@ -0,0 +1,257 @@
+import groovy.util.*
+import org.gradle.jvm.tasks.Jar
+import org.gradle.kotlin.dsl.*
+import org.gradle.plugins.signing.*
+import org.jetbrains.kotlin.gradle.dsl.*
+import org.jetbrains.kotlin.gradle.tasks.*
+import java.net.*
+
+/*
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+// Configures publishing of Maven artifacts to MavenCentral
+plugins {
+    `maven-publish`
+    signing
+}
+
+val isMultiplatform = name in listOf(
+    "kotlinx-serialization-core",
+    "kotlinx-serialization-json",
+    "kotlinx-serialization-json-okio",
+    "kotlinx-serialization-json-io",
+    "kotlinx-serialization-json-tests",
+    "kotlinx-serialization-protobuf",
+    "kotlinx-serialization-cbor",
+    "kotlinx-serialization-properties"
+)
+
+val isBom = name == "kotlinx-serialization-bom"
+
+if (!isBom) {
+    tasks.register<Jar>("stubJavadoc") {
+        archiveClassifier = "javadoc"
+    }
+}
+
+tasks.register<Jar>("emptyJar")
+
+afterEvaluate {
+    val mainSourcesJar = tasks.register<Jar>("mainSourcesJar") {
+        archiveClassifier = "sources"
+        if (isMultiplatform) {
+            from(kotlinExtension.sourceSets.getByName("commonMain").kotlin)
+        } else if (isBom) {
+            // no-op: sourceSets is [] for BOM, as it does not have sources.
+        } else {
+            from(sourceSets.named("main").get().allSource)
+        }
+    }
+
+    publishing {
+        if (!isMultiplatform && !isBom) {
+            publications.register<MavenPublication>("maven") {
+                artifactId = project.name
+                from(components["java"])
+                artifact(mainSourcesJar)
+                artifact(tasks.named("stubJavadoc"))
+            }
+        } else {
+            // Rename artifacts for backward compatibility
+            publications.withType<MavenPublication>().configureEach {
+                val type = name
+                logger.info("Configuring $type")
+                when (type) {
+                    "kotlinMultiplatform" -> {
+                        // With Kotlin 1.4.0, the root module ID has no suffix, but for compatibility with
+                        // the consumers who can't read Gradle module metadata, we publish the JVM artifacts in it
+                        artifactId = project.name
+                        reconfigureMultiplatformPublication(publications.getByName("jvm") as MavenPublication)
+                    }
+                    "metadata", "jvm", "js", "native" -> artifactId = "${project.name}-$type"
+                }
+                logger.info("Artifact id = $artifactId")
+
+                // The 'root' module publishes the JVM module's Javadoc JAR as per reconfigureMultiplatformPublication, and
+                // every other module should publish an empty Javadoc JAR. TODO: provide proper documentation artifacts?
+                if (name != "kotlinMultiplatform" && !isBom) {
+                    artifact(tasks.named("stubJavadoc"))
+                }
+            }
+        }
+
+        publications.withType<MavenPublication>().configureEach {
+            pom.configureMavenCentralMetadata()
+            signPublicationIfKeyPresent()
+        }
+    }
+}
+
+val testRepositoryDir = project.layout.buildDirectory.dir("testRepository")
+
+publishing {
+    repositories {
+        addSonatypeRepository()
+
+        /**
+         * Maven repository in build directory to check published artifacts.
+         */
+        maven {
+            setUrl(testRepositoryDir)
+            name = "test"
+        }
+    }
+}
+
+interface LocalArtifactAttr : Named {
+    companion object {
+        val ATTRIBUTE = Attribute.of(
+            "kotlinx.kover.gradle-plugin",
+            LocalArtifactAttr::class.java
+        )
+    }
+}
+
+val testPublicationTask: TaskCollection<*> = tasks.named { name -> name == "publishAllPublicationsToTestRepository" }
+configurations.register("testPublication") {
+    isVisible = false
+    isCanBeResolved = false
+    // this configuration produces modules that can be consumed by other projects
+    isCanBeConsumed = true
+    attributes {
+        attribute(Attribute.of("kotlinx.serialization.repository", String::class.java), "test")
+    }
+    outgoing.artifact(testRepositoryDir) {
+        builtBy(testPublicationTask)
+    }
+}
+
+tasks.withType<AbstractPublishToMaven>().configureEach {
+    dependsOn(tasks.withType<Sign>())
+}
+
+// NOTE: This is a temporary WA, see KT-61313.
+// Task ':compileTestKotlin<platform>' uses this output of task ':sign<platform>Publication' without declaring an explicit or implicit dependency
+tasks.withType<KotlinNativeCompile>().matching { it.name.startsWith("compileTestKotlin") }.configureEach {
+    val targetName = name.substringAfter("compileTestKotlin")
+    mustRunAfter(tasks.withType<Sign>().named { it == "sign${targetName}Publication" })
+}
+
+// NOTE: This is a temporary WA, see KT-61313.
+// Task ':linkDebugTest<platform>' uses this output of task ':sign<platform>Publication' without declaring an explicit or implicit dependency
+tasks.withType<KotlinNativeLink>() {
+    val targetName = name.substringAfter("linkDebugTest")
+    mustRunAfter(tasks.withType<Sign>().named { it == "sign${targetName}Publication" })
+}
+
+fun MavenPom.configureMavenCentralMetadata() {
+    name = project.name
+    description = "Kotlin multiplatform serialization runtime library"
+    url = "https://github.com/Kotlin/kotlinx.serialization"
+
+    licenses {
+        license {
+            name = "The Apache Software License, Version 2.0"
+            url = "https://www.apache.org/licenses/LICENSE-2.0.txt"
+            distribution = "repo"
+        }
+    }
+
+    developers {
+        developer {
+            id = "JetBrains"
+            name = "JetBrains Team"
+            organization = "JetBrains"
+            organizationUrl = "https://www.jetbrains.com"
+        }
+    }
+
+    scm {
+        url = "https://github.com/Kotlin/kotlinx.serialization"
+    }
+}
+
+// utility functions
+
+/**
+ * Re-configure common publication to depend on JVM artifact only in pom.xml.
+ *
+ *  Publish the platform JAR and POM so that consumers who depend on this module and can't read Gradle module
+ *  metadata can still get the platform artifact and transitive dependencies from the POM.
+ *
+ *  Taken from https://github.com/Kotlin/kotlinx.coroutines
+ */
+public fun Project.reconfigureMultiplatformPublication(jvmPublication: MavenPublication) {
+    val mavenPublications =
+        extensions.getByType<PublishingExtension>().publications.withType<MavenPublication>()
+    val kmpPublication = mavenPublications.getByName("kotlinMultiplatform")
+
+    var jvmPublicationXml: XmlProvider? = null
+    jvmPublication.pom.withXml { jvmPublicationXml = this }
+
+    kmpPublication.pom.withXml {
+        val root = asNode()
+        // Remove the original content and add the content from the platform POM:
+        root.children().toList().forEach { root.remove(it as Node) }
+        jvmPublicationXml!!.asNode().children().forEach { root.append(it as Node) }
+
+        // Adjust the self artifact ID, as it should match the root module's coordinates:
+        ((root["artifactId"] as NodeList).first() as Node).setValue(kmpPublication.artifactId)
+
+        // Set packaging to POM to indicate that there's no artifact:
+        root.appendNode("packaging", "pom")
+
+        // Remove the original platform dependencies and add a single dependency on the platform module:
+        val dependencies = (root["dependencies"] as NodeList).first() as Node
+        dependencies.children().toList().forEach { dependencies.remove(it as Node) }
+        dependencies.appendNode("dependency").apply {
+            appendNode("groupId", jvmPublication.groupId)
+            appendNode("artifactId", jvmPublication.artifactId)
+            appendNode("version", jvmPublication.version)
+            appendNode("scope", "compile")
+        }
+    }
+
+    // TODO verify if this is still relevant
+    tasks.matching { it.name == "generatePomFileForKotlinMultiplatformPublication" }.configureEach {
+        @Suppress("DEPRECATION")
+        dependsOn("generatePomFileFor${jvmPublication.name.capitalize()}Publication")
+    }
+}
+
+fun MavenPublication.signPublicationIfKeyPresent() {
+    val keyId = getSensitiveProperty("libs.sign.key.id")
+    val signingKey = getSensitiveProperty("libs.sign.key.private")
+    val signingKeyPassphrase = getSensitiveProperty("libs.sign.passphrase")
+    if (!signingKey.isNullOrBlank()) {
+        extensions.configure<SigningExtension>("signing") {
+            useInMemoryPgpKeys(keyId, signingKey, signingKeyPassphrase)
+            sign(this@signPublicationIfKeyPresent)
+        }
+    }
+}
+
+fun RepositoryHandler.addSonatypeRepository() {
+    maven {
+        url = mavenRepositoryUri()
+        credentials {
+            username = getSensitiveProperty("libs.sonatype.user")
+            password = getSensitiveProperty("libs.sonatype.password")
+        }
+    }
+}
+
+fun mavenRepositoryUri(): URI {
+    // TODO -SNAPSHOT detection can be made here as well
+    val repositoryId: String? = System.getenv("libs.repository.id")
+    return if (repositoryId == null) {
+        URI("https://oss.sonatype.org/service/local/staging/deploy/maven2/")
+    } else {
+        URI("https://oss.sonatype.org/service/local/staging/deployByRepositoryId/$repositoryId")
+    }
+}
+
+fun getSensitiveProperty(name: String): String? {
+    return findProperty(name) as? String ?: System.getenv(name)
+}
diff --git a/buildSrc/src/main/kotlin/setupJavaPlugin.kt b/buildSrc/src/main/kotlin/setupJavaPlugin.kt
deleted file mode 100644
index 40db563..0000000
--- a/buildSrc/src/main/kotlin/setupJavaPlugin.kt
+++ /dev/null
@@ -1,42 +0,0 @@
-import org.gradle.api.*
-import org.gradle.api.file.*
-import org.gradle.api.plugins.*
-import org.gradle.api.tasks.*
-import org.gradle.api.tasks.testing.*
-import org.gradle.jvm.tasks.*
-import org.jetbrains.kotlin.gradle.plugin.*
-
-/*
- * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-object JavaPluginUtil {
-
-    @JvmStatic
-    fun Project.applyJavaPlugin() {
-        plugins.apply("java")
-
-        plugins.withId("org.jetbrains.kotlin.multiplatform") {
-            listOf(
-                JavaPlugin.API_ELEMENTS_CONFIGURATION_NAME,
-                JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME
-            ).forEach { outputConfigurationName ->
-                configurations.findByName(outputConfigurationName)?.isCanBeConsumed = false
-            }
-
-            disableJavaPluginTasks(extensions.getByName("sourceSets") as SourceSetContainer)
-        }
-    }
-}
-
-private fun Project.disableJavaPluginTasks(javaSourceSet: SourceSetContainer) {
-    project.tasks.withType(Jar::class.java).named(javaSourceSet.getByName("main").jarTaskName).configure {
-        dependsOn("jvmTest")
-        enabled = false
-    }
-
-    project.tasks.withType(Test::class.java).named(JavaPlugin.TEST_TASK_NAME) {
-        dependsOn("jvmJar")
-        enabled = false
-    }
-}
diff --git a/buildSrc/src/main/kotlin/source-sets-conventions.gradle.kts b/buildSrc/src/main/kotlin/source-sets-conventions.gradle.kts
new file mode 100644
index 0000000..1b2a75e
--- /dev/null
+++ b/buildSrc/src/main/kotlin/source-sets-conventions.gradle.kts
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+@file:OptIn(ExperimentalWasmDsl::class)
+
+import org.gradle.kotlin.dsl.*
+import org.jetbrains.kotlin.gradle.*
+import org.jetbrains.kotlin.gradle.dsl.*
+import org.jetbrains.kotlin.gradle.plugin.mpp.*
+import org.jetbrains.kotlin.gradle.targets.js.dsl.ExperimentalWasmDsl
+import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension
+import org.jetbrains.kotlin.gradle.targets.native.tasks.*
+import org.jetbrains.kotlin.gradle.tasks.*
+import org.jetbrains.kotlin.gradle.testing.*
+
+plugins {
+    kotlin("multiplatform")
+}
+
+tasks.withType<JavaCompile>().configureEach {
+    options.release = 8
+}
+
+internal fun Project.versionCatalog(): VersionCatalog = versionCatalogs.named("libs")
+
+kotlin {
+    explicitApi()
+
+    jvm {
+        withJava()
+        @OptIn(ExperimentalKotlinGradlePluginApi::class)
+        compilerOptions {
+            jvmTarget = JvmTarget.JVM_1_8
+            freeCompilerArgs.add("-Xjdk-release=1.8")
+        }
+    }
+    jvmToolchain(jdkToolchainVersion)
+
+    js {
+        nodejs {
+            testTask {
+                useMocha {
+                    timeout = "10s"
+                }
+            }
+        }
+
+        @OptIn(ExperimentalKotlinGradlePluginApi::class)
+        compilerOptions {
+            sourceMap = true
+            moduleKind = JsModuleKind.MODULE_UMD
+        }
+    }
+
+    wasmJs {
+        nodejs()
+    }
+
+    wasmWasi {
+        nodejs()
+    }
+
+    sourceSets.all {
+        kotlin.srcDirs("$name/src")
+        resources.srcDirs("$name/resources")
+        languageSettings {
+            progressiveMode = true
+
+            optIn("kotlin.ExperimentalMultiplatform")
+            optIn("kotlinx.serialization.InternalSerializationApi")
+        }
+    }
+
+    sourceSets {
+        commonMain {
+            dependencies {
+                api(versionCatalog().findLibrary("kotlin.stdlib").get())
+            }
+        }
+
+        commonTest {
+            dependencies {
+                api(versionCatalog().findLibrary("kotlin.test").get())
+            }
+        }
+
+        register("wasmMain") {
+            dependsOn(commonMain.get())
+        }
+        register("wasmTest") {
+            dependsOn(commonTest.get())
+        }
+
+        named("wasmJsMain") {
+            dependsOn(named("wasmMain").get())
+        }
+
+        named("wasmJsTest") {
+            dependsOn(named("wasmTest").get())
+        }
+
+        named("wasmWasiMain") {
+            dependsOn(named("wasmMain").get())
+        }
+
+        named("wasmWasiTest") {
+            dependsOn(named("wasmTest").get())
+        }
+    }
+
+    sourceSets.matching({ it.name.contains("Test") }).configureEach {
+        languageSettings {
+            optIn("kotlinx.serialization.InternalSerializationApi")
+            optIn("kotlinx.serialization.ExperimentalSerializationApi")
+        }
+    }
+}
+
+tasks.withType(KotlinCompilationTask::class).configureEach {
+    compilerOptions {
+        val isMainTaskName = name.startsWith("compileKotlin")
+        if (isMainTaskName) {
+            allWarningsAsErrors = true
+        }
+        if (overriddenLanguageVersion != null) {
+            languageVersion = KotlinVersion.fromVersion(overriddenLanguageVersion!!)
+            freeCompilerArgs.add("-Xsuppress-version-warnings")
+        }
+        freeCompilerArgs.add("-Xexpect-actual-classes")
+    }
+}
diff --git a/buildSrc/src/main/kotlin/teamcity-conventions.gradle.kts b/buildSrc/src/main/kotlin/teamcity-conventions.gradle.kts
new file mode 100644
index 0000000..47e2882
--- /dev/null
+++ b/buildSrc/src/main/kotlin/teamcity-conventions.gradle.kts
@@ -0,0 +1,16 @@
+/*
+ * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+import org.gradle.kotlin.dsl.*
+
+val teamcitySuffix = findProperty("teamcitySuffix")?.toString()
+if (teamcityInteractionEnabled && hasProperty("teamcity") && !propertyIsTrue("build_snapshot_train")) {
+    // Tell teamcity about version number
+    val postfix = if (teamcitySuffix == null) "" else " ($teamcitySuffix)"
+    println("##teamcity[buildNumber '${project.version}${postfix}']")
+
+    gradle.taskGraph.beforeTask {
+        println("##teamcity[progressMessage 'Gradle: ${path}:${name}']")
+    }
+}
diff --git a/core/api/kotlinx-serialization-core.api b/core/api/kotlinx-serialization-core.api
index 720e584..0ac51c8 100644
--- a/core/api/kotlinx-serialization-core.api
+++ b/core/api/kotlinx-serialization-core.api
@@ -123,6 +123,8 @@
 }
 
 public final class kotlinx/serialization/SerializersKt {
+	public static final fun moduleThenPolymorphic (Lkotlinx/serialization/modules/SerializersModule;Lkotlin/reflect/KClass;)Lkotlinx/serialization/KSerializer;
+	public static final fun moduleThenPolymorphic (Lkotlinx/serialization/modules/SerializersModule;Lkotlin/reflect/KClass;[Lkotlinx/serialization/KSerializer;)Lkotlinx/serialization/KSerializer;
 	public static final fun noCompiledSerializer (Ljava/lang/String;)Lkotlinx/serialization/KSerializer;
 	public static final fun noCompiledSerializer (Lkotlinx/serialization/modules/SerializersModule;Lkotlin/reflect/KClass;)Lkotlinx/serialization/KSerializer;
 	public static final fun noCompiledSerializer (Lkotlinx/serialization/modules/SerializersModule;Lkotlin/reflect/KClass;[Lkotlinx/serialization/KSerializer;)Lkotlinx/serialization/KSerializer;
@@ -197,6 +199,7 @@
 	public static final fun serializer (Lkotlin/jvm/internal/ShortCompanionObject;)Lkotlinx/serialization/KSerializer;
 	public static final fun serializer (Lkotlin/jvm/internal/StringCompanionObject;)Lkotlinx/serialization/KSerializer;
 	public static final fun serializer (Lkotlin/time/Duration$Companion;)Lkotlinx/serialization/KSerializer;
+	public static final fun serializer (Lkotlin/uuid/Uuid$Companion;)Lkotlinx/serialization/KSerializer;
 }
 
 public final class kotlinx/serialization/builtins/LongAsStringSerializer : kotlinx/serialization/KSerializer {
@@ -306,6 +309,7 @@
 	public static synthetic fun buildClassSerialDescriptor$default (Ljava/lang/String;[Lkotlinx/serialization/descriptors/SerialDescriptor;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlinx/serialization/descriptors/SerialDescriptor;
 	public static final fun buildSerialDescriptor (Ljava/lang/String;Lkotlinx/serialization/descriptors/SerialKind;[Lkotlinx/serialization/descriptors/SerialDescriptor;Lkotlin/jvm/functions/Function1;)Lkotlinx/serialization/descriptors/SerialDescriptor;
 	public static synthetic fun buildSerialDescriptor$default (Ljava/lang/String;Lkotlinx/serialization/descriptors/SerialKind;[Lkotlinx/serialization/descriptors/SerialDescriptor;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlinx/serialization/descriptors/SerialDescriptor;
+	public static final fun getNonNullOriginal (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/descriptors/SerialDescriptor;
 	public static final fun getNullable (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/descriptors/SerialDescriptor;
 	public static final fun listSerialDescriptor (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/descriptors/SerialDescriptor;
 	public static final fun mapSerialDescriptor (Lkotlinx/serialization/descriptors/SerialDescriptor;Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/descriptors/SerialDescriptor;
@@ -722,6 +726,12 @@
 	public fun toString ()Ljava/lang/String;
 }
 
+public final class kotlinx/serialization/internal/EnumsKt {
+	public static final fun createAnnotatedEnumSerializer (Ljava/lang/String;[Ljava/lang/Enum;[Ljava/lang/String;[[Ljava/lang/annotation/Annotation;[Ljava/lang/annotation/Annotation;)Lkotlinx/serialization/KSerializer;
+	public static final fun createMarkedEnumSerializer (Ljava/lang/String;[Ljava/lang/Enum;[Ljava/lang/String;[[Ljava/lang/annotation/Annotation;)Lkotlinx/serialization/KSerializer;
+	public static final fun createSimpleEnumSerializer (Ljava/lang/String;[Ljava/lang/Enum;)Lkotlinx/serialization/KSerializer;
+}
+
 public final class kotlinx/serialization/internal/FloatArrayBuilder : kotlinx/serialization/internal/PrimitiveArrayBuilder {
 	public synthetic fun build$kotlinx_serialization_core ()Ljava/lang/Object;
 }
@@ -905,6 +915,7 @@
 	public synthetic fun getTag (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Ljava/lang/Object;
 	protected final fun getTag (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Ljava/lang/String;
 	protected final fun nested (Ljava/lang/String;)Ljava/lang/String;
+	protected final fun renderTagStack ()Ljava/lang/String;
 }
 
 public abstract class kotlinx/serialization/internal/NamedValueEncoder : kotlinx/serialization/internal/TaggedEncoder {
@@ -1283,6 +1294,15 @@
 	public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lkotlin/Unit;)V
 }
 
+public final class kotlinx/serialization/internal/UuidSerializer : kotlinx/serialization/KSerializer {
+	public static final field INSTANCE Lkotlinx/serialization/internal/UuidSerializer;
+	public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
+	public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lkotlin/uuid/Uuid;
+	public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
+	public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
+	public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lkotlin/uuid/Uuid;)V
+}
+
 public final class kotlinx/serialization/modules/PolymorphicModuleBuilder {
 	public fun <init> (Lkotlin/reflect/KClass;Lkotlinx/serialization/KSerializer;)V
 	public synthetic fun <init> (Lkotlin/reflect/KClass;Lkotlinx/serialization/KSerializer;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
diff --git a/core/api/kotlinx-serialization-core.klib.api b/core/api/kotlinx-serialization-core.klib.api
new file mode 100644
index 0000000..c640b60
--- /dev/null
+++ b/core/api/kotlinx-serialization-core.klib.api
@@ -0,0 +1,1204 @@
+// Klib ABI Dump
+// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, js, linuxArm32Hfp, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, wasmJs, wasmWasi, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64]
+// Alias: native => [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, linuxArm32Hfp, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64]
+// Rendering settings:
+// - Signature version: 2
+// - Show manifest properties: true
+// - Show declarations: true
+
+// Library unique name: <org.jetbrains.kotlinx:kotlinx-serialization-core>
+open annotation class kotlinx.serialization.internal/NamedCompanion : kotlin/Annotation { // kotlinx.serialization.internal/NamedCompanion|null[0]
+    constructor <init>() // kotlinx.serialization.internal/NamedCompanion.<init>|<init>(){}[0]
+}
+
+open annotation class kotlinx.serialization/Contextual : kotlin/Annotation { // kotlinx.serialization/Contextual|null[0]
+    constructor <init>() // kotlinx.serialization/Contextual.<init>|<init>(){}[0]
+}
+
+open annotation class kotlinx.serialization/EncodeDefault : kotlin/Annotation { // kotlinx.serialization/EncodeDefault|null[0]
+    constructor <init>(kotlinx.serialization/EncodeDefault.Mode = ...) // kotlinx.serialization/EncodeDefault.<init>|<init>(kotlinx.serialization.EncodeDefault.Mode){}[0]
+
+    final val mode // kotlinx.serialization/EncodeDefault.mode|{}mode[0]
+        final fun <get-mode>(): kotlinx.serialization/EncodeDefault.Mode // kotlinx.serialization/EncodeDefault.mode.<get-mode>|<get-mode>(){}[0]
+
+    final enum class Mode : kotlin/Enum<kotlinx.serialization/EncodeDefault.Mode> { // kotlinx.serialization/EncodeDefault.Mode|null[0]
+        enum entry ALWAYS // kotlinx.serialization/EncodeDefault.Mode.ALWAYS|null[0]
+        enum entry NEVER // kotlinx.serialization/EncodeDefault.Mode.NEVER|null[0]
+
+        final val entries // kotlinx.serialization/EncodeDefault.Mode.entries|#static{}entries[0]
+            final fun <get-entries>(): kotlin.enums/EnumEntries<kotlinx.serialization/EncodeDefault.Mode> // kotlinx.serialization/EncodeDefault.Mode.entries.<get-entries>|<get-entries>#static(){}[0]
+
+        final fun valueOf(kotlin/String): kotlinx.serialization/EncodeDefault.Mode // kotlinx.serialization/EncodeDefault.Mode.valueOf|valueOf#static(kotlin.String){}[0]
+        final fun values(): kotlin/Array<kotlinx.serialization/EncodeDefault.Mode> // kotlinx.serialization/EncodeDefault.Mode.values|values#static(){}[0]
+    }
+}
+
+open annotation class kotlinx.serialization/ExperimentalSerializationApi : kotlin/Annotation { // kotlinx.serialization/ExperimentalSerializationApi|null[0]
+    constructor <init>() // kotlinx.serialization/ExperimentalSerializationApi.<init>|<init>(){}[0]
+}
+
+open annotation class kotlinx.serialization/InheritableSerialInfo : kotlin/Annotation { // kotlinx.serialization/InheritableSerialInfo|null[0]
+    constructor <init>() // kotlinx.serialization/InheritableSerialInfo.<init>|<init>(){}[0]
+}
+
+open annotation class kotlinx.serialization/InternalSerializationApi : kotlin/Annotation { // kotlinx.serialization/InternalSerializationApi|null[0]
+    constructor <init>() // kotlinx.serialization/InternalSerializationApi.<init>|<init>(){}[0]
+}
+
+open annotation class kotlinx.serialization/KeepGeneratedSerializer : kotlin/Annotation { // kotlinx.serialization/KeepGeneratedSerializer|null[0]
+    constructor <init>() // kotlinx.serialization/KeepGeneratedSerializer.<init>|<init>(){}[0]
+}
+
+open annotation class kotlinx.serialization/MetaSerializable : kotlin/Annotation { // kotlinx.serialization/MetaSerializable|null[0]
+    constructor <init>() // kotlinx.serialization/MetaSerializable.<init>|<init>(){}[0]
+}
+
+open annotation class kotlinx.serialization/Polymorphic : kotlin/Annotation { // kotlinx.serialization/Polymorphic|null[0]
+    constructor <init>() // kotlinx.serialization/Polymorphic.<init>|<init>(){}[0]
+}
+
+open annotation class kotlinx.serialization/Required : kotlin/Annotation { // kotlinx.serialization/Required|null[0]
+    constructor <init>() // kotlinx.serialization/Required.<init>|<init>(){}[0]
+}
+
+open annotation class kotlinx.serialization/SerialInfo : kotlin/Annotation { // kotlinx.serialization/SerialInfo|null[0]
+    constructor <init>() // kotlinx.serialization/SerialInfo.<init>|<init>(){}[0]
+}
+
+open annotation class kotlinx.serialization/SerialName : kotlin/Annotation { // kotlinx.serialization/SerialName|null[0]
+    constructor <init>(kotlin/String) // kotlinx.serialization/SerialName.<init>|<init>(kotlin.String){}[0]
+
+    final val value // kotlinx.serialization/SerialName.value|{}value[0]
+        final fun <get-value>(): kotlin/String // kotlinx.serialization/SerialName.value.<get-value>|<get-value>(){}[0]
+}
+
+open annotation class kotlinx.serialization/Serializable : kotlin/Annotation { // kotlinx.serialization/Serializable|null[0]
+    constructor <init>(kotlin.reflect/KClass<out kotlinx.serialization/KSerializer<*>> = ...) // kotlinx.serialization/Serializable.<init>|<init>(kotlin.reflect.KClass<out|kotlinx.serialization.KSerializer<*>>){}[0]
+
+    final val with // kotlinx.serialization/Serializable.with|{}with[0]
+        final fun <get-with>(): kotlin.reflect/KClass<out kotlinx.serialization/KSerializer<*>> // kotlinx.serialization/Serializable.with.<get-with>|<get-with>(){}[0]
+}
+
+open annotation class kotlinx.serialization/SerializableWith : kotlin/Annotation { // kotlinx.serialization/SerializableWith|null[0]
+    constructor <init>(kotlin.reflect/KClass<out kotlinx.serialization/KSerializer<*>>) // kotlinx.serialization/SerializableWith.<init>|<init>(kotlin.reflect.KClass<out|kotlinx.serialization.KSerializer<*>>){}[0]
+
+    final val serializer // kotlinx.serialization/SerializableWith.serializer|{}serializer[0]
+        final fun <get-serializer>(): kotlin.reflect/KClass<out kotlinx.serialization/KSerializer<*>> // kotlinx.serialization/SerializableWith.serializer.<get-serializer>|<get-serializer>(){}[0]
+}
+
+open annotation class kotlinx.serialization/Serializer : kotlin/Annotation { // kotlinx.serialization/Serializer|null[0]
+    constructor <init>(kotlin.reflect/KClass<*>) // kotlinx.serialization/Serializer.<init>|<init>(kotlin.reflect.KClass<*>){}[0]
+
+    final val forClass // kotlinx.serialization/Serializer.forClass|{}forClass[0]
+        final fun <get-forClass>(): kotlin.reflect/KClass<*> // kotlinx.serialization/Serializer.forClass.<get-forClass>|<get-forClass>(){}[0]
+}
+
+open annotation class kotlinx.serialization/Transient : kotlin/Annotation { // kotlinx.serialization/Transient|null[0]
+    constructor <init>() // kotlinx.serialization/Transient.<init>|<init>(){}[0]
+}
+
+open annotation class kotlinx.serialization/UseContextualSerialization : kotlin/Annotation { // kotlinx.serialization/UseContextualSerialization|null[0]
+    constructor <init>(kotlin/Array<out kotlin.reflect/KClass<*>>...) // kotlinx.serialization/UseContextualSerialization.<init>|<init>(kotlin.Array<out|kotlin.reflect.KClass<*>>...){}[0]
+
+    final val forClasses // kotlinx.serialization/UseContextualSerialization.forClasses|{}forClasses[0]
+        final fun <get-forClasses>(): kotlin/Array<out kotlin.reflect/KClass<*>> // kotlinx.serialization/UseContextualSerialization.forClasses.<get-forClasses>|<get-forClasses>(){}[0]
+}
+
+open annotation class kotlinx.serialization/UseSerializers : kotlin/Annotation { // kotlinx.serialization/UseSerializers|null[0]
+    constructor <init>(kotlin/Array<out kotlin.reflect/KClass<out kotlinx.serialization/KSerializer<*>>>...) // kotlinx.serialization/UseSerializers.<init>|<init>(kotlin.Array<out|kotlin.reflect.KClass<out|kotlinx.serialization.KSerializer<*>>>...){}[0]
+
+    final val serializerClasses // kotlinx.serialization/UseSerializers.serializerClasses|{}serializerClasses[0]
+        final fun <get-serializerClasses>(): kotlin/Array<out kotlin.reflect/KClass<out kotlinx.serialization/KSerializer<*>>> // kotlinx.serialization/UseSerializers.serializerClasses.<get-serializerClasses>|<get-serializerClasses>(){}[0]
+}
+
+abstract interface <#A: in kotlin/Any?> kotlinx.serialization/SerializationStrategy { // kotlinx.serialization/SerializationStrategy|null[0]
+    abstract val descriptor // kotlinx.serialization/SerializationStrategy.descriptor|{}descriptor[0]
+        abstract fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization/SerializationStrategy.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+
+    abstract fun serialize(kotlinx.serialization.encoding/Encoder, #A) // kotlinx.serialization/SerializationStrategy.serialize|serialize(kotlinx.serialization.encoding.Encoder;1:0){}[0]
+}
+
+abstract interface <#A: kotlin/Any?> kotlinx.serialization.internal/GeneratedSerializer : kotlinx.serialization/KSerializer<#A> { // kotlinx.serialization.internal/GeneratedSerializer|null[0]
+    abstract fun childSerializers(): kotlin/Array<kotlinx.serialization/KSerializer<*>> // kotlinx.serialization.internal/GeneratedSerializer.childSerializers|childSerializers(){}[0]
+    open fun typeParametersSerializers(): kotlin/Array<kotlinx.serialization/KSerializer<*>> // kotlinx.serialization.internal/GeneratedSerializer.typeParametersSerializers|typeParametersSerializers(){}[0]
+}
+
+abstract interface <#A: kotlin/Any?> kotlinx.serialization/KSerializer : kotlinx.serialization/DeserializationStrategy<#A>, kotlinx.serialization/SerializationStrategy<#A> { // kotlinx.serialization/KSerializer|null[0]
+    abstract val descriptor // kotlinx.serialization/KSerializer.descriptor|{}descriptor[0]
+        abstract fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization/KSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+}
+
+abstract interface <#A: out kotlin/Any?> kotlinx.serialization/DeserializationStrategy { // kotlinx.serialization/DeserializationStrategy|null[0]
+    abstract val descriptor // kotlinx.serialization/DeserializationStrategy.descriptor|{}descriptor[0]
+        abstract fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization/DeserializationStrategy.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+
+    abstract fun deserialize(kotlinx.serialization.encoding/Decoder): #A // kotlinx.serialization/DeserializationStrategy.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
+}
+
+abstract interface kotlinx.serialization.descriptors/SerialDescriptor { // kotlinx.serialization.descriptors/SerialDescriptor|null[0]
+    abstract val elementsCount // kotlinx.serialization.descriptors/SerialDescriptor.elementsCount|{}elementsCount[0]
+        abstract fun <get-elementsCount>(): kotlin/Int // kotlinx.serialization.descriptors/SerialDescriptor.elementsCount.<get-elementsCount>|<get-elementsCount>(){}[0]
+    abstract val kind // kotlinx.serialization.descriptors/SerialDescriptor.kind|{}kind[0]
+        abstract fun <get-kind>(): kotlinx.serialization.descriptors/SerialKind // kotlinx.serialization.descriptors/SerialDescriptor.kind.<get-kind>|<get-kind>(){}[0]
+    abstract val serialName // kotlinx.serialization.descriptors/SerialDescriptor.serialName|{}serialName[0]
+        abstract fun <get-serialName>(): kotlin/String // kotlinx.serialization.descriptors/SerialDescriptor.serialName.<get-serialName>|<get-serialName>(){}[0]
+    open val annotations // kotlinx.serialization.descriptors/SerialDescriptor.annotations|{}annotations[0]
+        open fun <get-annotations>(): kotlin.collections/List<kotlin/Annotation> // kotlinx.serialization.descriptors/SerialDescriptor.annotations.<get-annotations>|<get-annotations>(){}[0]
+    open val isInline // kotlinx.serialization.descriptors/SerialDescriptor.isInline|{}isInline[0]
+        open fun <get-isInline>(): kotlin/Boolean // kotlinx.serialization.descriptors/SerialDescriptor.isInline.<get-isInline>|<get-isInline>(){}[0]
+    open val isNullable // kotlinx.serialization.descriptors/SerialDescriptor.isNullable|{}isNullable[0]
+        open fun <get-isNullable>(): kotlin/Boolean // kotlinx.serialization.descriptors/SerialDescriptor.isNullable.<get-isNullable>|<get-isNullable>(){}[0]
+
+    abstract fun getElementAnnotations(kotlin/Int): kotlin.collections/List<kotlin/Annotation> // kotlinx.serialization.descriptors/SerialDescriptor.getElementAnnotations|getElementAnnotations(kotlin.Int){}[0]
+    abstract fun getElementDescriptor(kotlin/Int): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.descriptors/SerialDescriptor.getElementDescriptor|getElementDescriptor(kotlin.Int){}[0]
+    abstract fun getElementIndex(kotlin/String): kotlin/Int // kotlinx.serialization.descriptors/SerialDescriptor.getElementIndex|getElementIndex(kotlin.String){}[0]
+    abstract fun getElementName(kotlin/Int): kotlin/String // kotlinx.serialization.descriptors/SerialDescriptor.getElementName|getElementName(kotlin.Int){}[0]
+    abstract fun isElementOptional(kotlin/Int): kotlin/Boolean // kotlinx.serialization.descriptors/SerialDescriptor.isElementOptional|isElementOptional(kotlin.Int){}[0]
+}
+
+abstract interface kotlinx.serialization.encoding/ChunkedDecoder { // kotlinx.serialization.encoding/ChunkedDecoder|null[0]
+    abstract fun decodeStringChunked(kotlin/Function1<kotlin/String, kotlin/Unit>) // kotlinx.serialization.encoding/ChunkedDecoder.decodeStringChunked|decodeStringChunked(kotlin.Function1<kotlin.String,kotlin.Unit>){}[0]
+}
+
+abstract interface kotlinx.serialization.encoding/CompositeDecoder { // kotlinx.serialization.encoding/CompositeDecoder|null[0]
+    abstract val serializersModule // kotlinx.serialization.encoding/CompositeDecoder.serializersModule|{}serializersModule[0]
+        abstract fun <get-serializersModule>(): kotlinx.serialization.modules/SerializersModule // kotlinx.serialization.encoding/CompositeDecoder.serializersModule.<get-serializersModule>|<get-serializersModule>(){}[0]
+
+    abstract fun <#A1: kotlin/Any> decodeNullableSerializableElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlinx.serialization/DeserializationStrategy<#A1?>, #A1? = ...): #A1? // kotlinx.serialization.encoding/CompositeDecoder.decodeNullableSerializableElement|decodeNullableSerializableElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlinx.serialization.DeserializationStrategy<0:0?>;0:0?){0§<kotlin.Any>}[0]
+    abstract fun <#A1: kotlin/Any?> decodeSerializableElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlinx.serialization/DeserializationStrategy<#A1>, #A1? = ...): #A1 // kotlinx.serialization.encoding/CompositeDecoder.decodeSerializableElement|decodeSerializableElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlinx.serialization.DeserializationStrategy<0:0>;0:0?){0§<kotlin.Any?>}[0]
+    abstract fun decodeBooleanElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Boolean // kotlinx.serialization.encoding/CompositeDecoder.decodeBooleanElement|decodeBooleanElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+    abstract fun decodeByteElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Byte // kotlinx.serialization.encoding/CompositeDecoder.decodeByteElement|decodeByteElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+    abstract fun decodeCharElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Char // kotlinx.serialization.encoding/CompositeDecoder.decodeCharElement|decodeCharElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+    abstract fun decodeDoubleElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Double // kotlinx.serialization.encoding/CompositeDecoder.decodeDoubleElement|decodeDoubleElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+    abstract fun decodeElementIndex(kotlinx.serialization.descriptors/SerialDescriptor): kotlin/Int // kotlinx.serialization.encoding/CompositeDecoder.decodeElementIndex|decodeElementIndex(kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+    abstract fun decodeFloatElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Float // kotlinx.serialization.encoding/CompositeDecoder.decodeFloatElement|decodeFloatElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+    abstract fun decodeInlineElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlinx.serialization.encoding/Decoder // kotlinx.serialization.encoding/CompositeDecoder.decodeInlineElement|decodeInlineElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+    abstract fun decodeIntElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Int // kotlinx.serialization.encoding/CompositeDecoder.decodeIntElement|decodeIntElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+    abstract fun decodeLongElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Long // kotlinx.serialization.encoding/CompositeDecoder.decodeLongElement|decodeLongElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+    abstract fun decodeShortElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Short // kotlinx.serialization.encoding/CompositeDecoder.decodeShortElement|decodeShortElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+    abstract fun decodeStringElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/String // kotlinx.serialization.encoding/CompositeDecoder.decodeStringElement|decodeStringElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+    abstract fun endStructure(kotlinx.serialization.descriptors/SerialDescriptor) // kotlinx.serialization.encoding/CompositeDecoder.endStructure|endStructure(kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+    open fun decodeCollectionSize(kotlinx.serialization.descriptors/SerialDescriptor): kotlin/Int // kotlinx.serialization.encoding/CompositeDecoder.decodeCollectionSize|decodeCollectionSize(kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+    open fun decodeSequentially(): kotlin/Boolean // kotlinx.serialization.encoding/CompositeDecoder.decodeSequentially|decodeSequentially(){}[0]
+
+    final object Companion { // kotlinx.serialization.encoding/CompositeDecoder.Companion|null[0]
+        final const val DECODE_DONE // kotlinx.serialization.encoding/CompositeDecoder.Companion.DECODE_DONE|{}DECODE_DONE[0]
+            final fun <get-DECODE_DONE>(): kotlin/Int // kotlinx.serialization.encoding/CompositeDecoder.Companion.DECODE_DONE.<get-DECODE_DONE>|<get-DECODE_DONE>(){}[0]
+        final const val UNKNOWN_NAME // kotlinx.serialization.encoding/CompositeDecoder.Companion.UNKNOWN_NAME|{}UNKNOWN_NAME[0]
+            final fun <get-UNKNOWN_NAME>(): kotlin/Int // kotlinx.serialization.encoding/CompositeDecoder.Companion.UNKNOWN_NAME.<get-UNKNOWN_NAME>|<get-UNKNOWN_NAME>(){}[0]
+    }
+}
+
+abstract interface kotlinx.serialization.encoding/CompositeEncoder { // kotlinx.serialization.encoding/CompositeEncoder|null[0]
+    abstract val serializersModule // kotlinx.serialization.encoding/CompositeEncoder.serializersModule|{}serializersModule[0]
+        abstract fun <get-serializersModule>(): kotlinx.serialization.modules/SerializersModule // kotlinx.serialization.encoding/CompositeEncoder.serializersModule.<get-serializersModule>|<get-serializersModule>(){}[0]
+
+    abstract fun <#A1: kotlin/Any> encodeNullableSerializableElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlinx.serialization/SerializationStrategy<#A1>, #A1?) // kotlinx.serialization.encoding/CompositeEncoder.encodeNullableSerializableElement|encodeNullableSerializableElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlinx.serialization.SerializationStrategy<0:0>;0:0?){0§<kotlin.Any>}[0]
+    abstract fun <#A1: kotlin/Any?> encodeSerializableElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlinx.serialization/SerializationStrategy<#A1>, #A1) // kotlinx.serialization.encoding/CompositeEncoder.encodeSerializableElement|encodeSerializableElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlinx.serialization.SerializationStrategy<0:0>;0:0){0§<kotlin.Any?>}[0]
+    abstract fun encodeBooleanElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Boolean) // kotlinx.serialization.encoding/CompositeEncoder.encodeBooleanElement|encodeBooleanElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Boolean){}[0]
+    abstract fun encodeByteElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Byte) // kotlinx.serialization.encoding/CompositeEncoder.encodeByteElement|encodeByteElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Byte){}[0]
+    abstract fun encodeCharElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Char) // kotlinx.serialization.encoding/CompositeEncoder.encodeCharElement|encodeCharElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Char){}[0]
+    abstract fun encodeDoubleElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Double) // kotlinx.serialization.encoding/CompositeEncoder.encodeDoubleElement|encodeDoubleElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Double){}[0]
+    abstract fun encodeFloatElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Float) // kotlinx.serialization.encoding/CompositeEncoder.encodeFloatElement|encodeFloatElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Float){}[0]
+    abstract fun encodeInlineElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlinx.serialization.encoding/Encoder // kotlinx.serialization.encoding/CompositeEncoder.encodeInlineElement|encodeInlineElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+    abstract fun encodeIntElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Int) // kotlinx.serialization.encoding/CompositeEncoder.encodeIntElement|encodeIntElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Int){}[0]
+    abstract fun encodeLongElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Long) // kotlinx.serialization.encoding/CompositeEncoder.encodeLongElement|encodeLongElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Long){}[0]
+    abstract fun encodeShortElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Short) // kotlinx.serialization.encoding/CompositeEncoder.encodeShortElement|encodeShortElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Short){}[0]
+    abstract fun encodeStringElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/String) // kotlinx.serialization.encoding/CompositeEncoder.encodeStringElement|encodeStringElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.String){}[0]
+    abstract fun endStructure(kotlinx.serialization.descriptors/SerialDescriptor) // kotlinx.serialization.encoding/CompositeEncoder.endStructure|endStructure(kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+    open fun shouldEncodeElementDefault(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Boolean // kotlinx.serialization.encoding/CompositeEncoder.shouldEncodeElementDefault|shouldEncodeElementDefault(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+}
+
+abstract interface kotlinx.serialization.encoding/Decoder { // kotlinx.serialization.encoding/Decoder|null[0]
+    abstract val serializersModule // kotlinx.serialization.encoding/Decoder.serializersModule|{}serializersModule[0]
+        abstract fun <get-serializersModule>(): kotlinx.serialization.modules/SerializersModule // kotlinx.serialization.encoding/Decoder.serializersModule.<get-serializersModule>|<get-serializersModule>(){}[0]
+
+    abstract fun beginStructure(kotlinx.serialization.descriptors/SerialDescriptor): kotlinx.serialization.encoding/CompositeDecoder // kotlinx.serialization.encoding/Decoder.beginStructure|beginStructure(kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+    abstract fun decodeBoolean(): kotlin/Boolean // kotlinx.serialization.encoding/Decoder.decodeBoolean|decodeBoolean(){}[0]
+    abstract fun decodeByte(): kotlin/Byte // kotlinx.serialization.encoding/Decoder.decodeByte|decodeByte(){}[0]
+    abstract fun decodeChar(): kotlin/Char // kotlinx.serialization.encoding/Decoder.decodeChar|decodeChar(){}[0]
+    abstract fun decodeDouble(): kotlin/Double // kotlinx.serialization.encoding/Decoder.decodeDouble|decodeDouble(){}[0]
+    abstract fun decodeEnum(kotlinx.serialization.descriptors/SerialDescriptor): kotlin/Int // kotlinx.serialization.encoding/Decoder.decodeEnum|decodeEnum(kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+    abstract fun decodeFloat(): kotlin/Float // kotlinx.serialization.encoding/Decoder.decodeFloat|decodeFloat(){}[0]
+    abstract fun decodeInline(kotlinx.serialization.descriptors/SerialDescriptor): kotlinx.serialization.encoding/Decoder // kotlinx.serialization.encoding/Decoder.decodeInline|decodeInline(kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+    abstract fun decodeInt(): kotlin/Int // kotlinx.serialization.encoding/Decoder.decodeInt|decodeInt(){}[0]
+    abstract fun decodeLong(): kotlin/Long // kotlinx.serialization.encoding/Decoder.decodeLong|decodeLong(){}[0]
+    abstract fun decodeNotNullMark(): kotlin/Boolean // kotlinx.serialization.encoding/Decoder.decodeNotNullMark|decodeNotNullMark(){}[0]
+    abstract fun decodeNull(): kotlin/Nothing? // kotlinx.serialization.encoding/Decoder.decodeNull|decodeNull(){}[0]
+    abstract fun decodeShort(): kotlin/Short // kotlinx.serialization.encoding/Decoder.decodeShort|decodeShort(){}[0]
+    abstract fun decodeString(): kotlin/String // kotlinx.serialization.encoding/Decoder.decodeString|decodeString(){}[0]
+    open fun <#A1: kotlin/Any> decodeNullableSerializableValue(kotlinx.serialization/DeserializationStrategy<#A1?>): #A1? // kotlinx.serialization.encoding/Decoder.decodeNullableSerializableValue|decodeNullableSerializableValue(kotlinx.serialization.DeserializationStrategy<0:0?>){0§<kotlin.Any>}[0]
+    open fun <#A1: kotlin/Any?> decodeSerializableValue(kotlinx.serialization/DeserializationStrategy<#A1>): #A1 // kotlinx.serialization.encoding/Decoder.decodeSerializableValue|decodeSerializableValue(kotlinx.serialization.DeserializationStrategy<0:0>){0§<kotlin.Any?>}[0]
+}
+
+abstract interface kotlinx.serialization.encoding/Encoder { // kotlinx.serialization.encoding/Encoder|null[0]
+    abstract val serializersModule // kotlinx.serialization.encoding/Encoder.serializersModule|{}serializersModule[0]
+        abstract fun <get-serializersModule>(): kotlinx.serialization.modules/SerializersModule // kotlinx.serialization.encoding/Encoder.serializersModule.<get-serializersModule>|<get-serializersModule>(){}[0]
+
+    abstract fun beginStructure(kotlinx.serialization.descriptors/SerialDescriptor): kotlinx.serialization.encoding/CompositeEncoder // kotlinx.serialization.encoding/Encoder.beginStructure|beginStructure(kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+    abstract fun encodeBoolean(kotlin/Boolean) // kotlinx.serialization.encoding/Encoder.encodeBoolean|encodeBoolean(kotlin.Boolean){}[0]
+    abstract fun encodeByte(kotlin/Byte) // kotlinx.serialization.encoding/Encoder.encodeByte|encodeByte(kotlin.Byte){}[0]
+    abstract fun encodeChar(kotlin/Char) // kotlinx.serialization.encoding/Encoder.encodeChar|encodeChar(kotlin.Char){}[0]
+    abstract fun encodeDouble(kotlin/Double) // kotlinx.serialization.encoding/Encoder.encodeDouble|encodeDouble(kotlin.Double){}[0]
+    abstract fun encodeEnum(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int) // kotlinx.serialization.encoding/Encoder.encodeEnum|encodeEnum(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+    abstract fun encodeFloat(kotlin/Float) // kotlinx.serialization.encoding/Encoder.encodeFloat|encodeFloat(kotlin.Float){}[0]
+    abstract fun encodeInline(kotlinx.serialization.descriptors/SerialDescriptor): kotlinx.serialization.encoding/Encoder // kotlinx.serialization.encoding/Encoder.encodeInline|encodeInline(kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+    abstract fun encodeInt(kotlin/Int) // kotlinx.serialization.encoding/Encoder.encodeInt|encodeInt(kotlin.Int){}[0]
+    abstract fun encodeLong(kotlin/Long) // kotlinx.serialization.encoding/Encoder.encodeLong|encodeLong(kotlin.Long){}[0]
+    abstract fun encodeNull() // kotlinx.serialization.encoding/Encoder.encodeNull|encodeNull(){}[0]
+    abstract fun encodeShort(kotlin/Short) // kotlinx.serialization.encoding/Encoder.encodeShort|encodeShort(kotlin.Short){}[0]
+    abstract fun encodeString(kotlin/String) // kotlinx.serialization.encoding/Encoder.encodeString|encodeString(kotlin.String){}[0]
+    open fun <#A1: kotlin/Any> encodeNullableSerializableValue(kotlinx.serialization/SerializationStrategy<#A1>, #A1?) // kotlinx.serialization.encoding/Encoder.encodeNullableSerializableValue|encodeNullableSerializableValue(kotlinx.serialization.SerializationStrategy<0:0>;0:0?){0§<kotlin.Any>}[0]
+    open fun <#A1: kotlin/Any?> encodeSerializableValue(kotlinx.serialization/SerializationStrategy<#A1>, #A1) // kotlinx.serialization.encoding/Encoder.encodeSerializableValue|encodeSerializableValue(kotlinx.serialization.SerializationStrategy<0:0>;0:0){0§<kotlin.Any?>}[0]
+    open fun beginCollection(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlinx.serialization.encoding/CompositeEncoder // kotlinx.serialization.encoding/Encoder.beginCollection|beginCollection(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+    open fun encodeNotNullMark() // kotlinx.serialization.encoding/Encoder.encodeNotNullMark|encodeNotNullMark(){}[0]
+}
+
+abstract interface kotlinx.serialization.internal/SerializerFactory { // kotlinx.serialization.internal/SerializerFactory|null[0]
+    abstract fun serializer(kotlin/Array<out kotlinx.serialization/KSerializer<*>>...): kotlinx.serialization/KSerializer<*> // kotlinx.serialization.internal/SerializerFactory.serializer|serializer(kotlin.Array<out|kotlinx.serialization.KSerializer<*>>...){}[0]
+}
+
+abstract interface kotlinx.serialization.modules/SerializersModuleCollector { // kotlinx.serialization.modules/SerializersModuleCollector|null[0]
+    abstract fun <#A1: kotlin/Any, #B1: #A1> polymorphic(kotlin.reflect/KClass<#A1>, kotlin.reflect/KClass<#B1>, kotlinx.serialization/KSerializer<#B1>) // kotlinx.serialization.modules/SerializersModuleCollector.polymorphic|polymorphic(kotlin.reflect.KClass<0:0>;kotlin.reflect.KClass<0:1>;kotlinx.serialization.KSerializer<0:1>){0§<kotlin.Any>;1§<0:0>}[0]
+    abstract fun <#A1: kotlin/Any> contextual(kotlin.reflect/KClass<#A1>, kotlin/Function1<kotlin.collections/List<kotlinx.serialization/KSerializer<*>>, kotlinx.serialization/KSerializer<*>>) // kotlinx.serialization.modules/SerializersModuleCollector.contextual|contextual(kotlin.reflect.KClass<0:0>;kotlin.Function1<kotlin.collections.List<kotlinx.serialization.KSerializer<*>>,kotlinx.serialization.KSerializer<*>>){0§<kotlin.Any>}[0]
+    abstract fun <#A1: kotlin/Any> polymorphicDefaultDeserializer(kotlin.reflect/KClass<#A1>, kotlin/Function1<kotlin/String?, kotlinx.serialization/DeserializationStrategy<#A1>?>) // kotlinx.serialization.modules/SerializersModuleCollector.polymorphicDefaultDeserializer|polymorphicDefaultDeserializer(kotlin.reflect.KClass<0:0>;kotlin.Function1<kotlin.String?,kotlinx.serialization.DeserializationStrategy<0:0>?>){0§<kotlin.Any>}[0]
+    abstract fun <#A1: kotlin/Any> polymorphicDefaultSerializer(kotlin.reflect/KClass<#A1>, kotlin/Function1<#A1, kotlinx.serialization/SerializationStrategy<#A1>?>) // kotlinx.serialization.modules/SerializersModuleCollector.polymorphicDefaultSerializer|polymorphicDefaultSerializer(kotlin.reflect.KClass<0:0>;kotlin.Function1<0:0,kotlinx.serialization.SerializationStrategy<0:0>?>){0§<kotlin.Any>}[0]
+    open fun <#A1: kotlin/Any> contextual(kotlin.reflect/KClass<#A1>, kotlinx.serialization/KSerializer<#A1>) // kotlinx.serialization.modules/SerializersModuleCollector.contextual|contextual(kotlin.reflect.KClass<0:0>;kotlinx.serialization.KSerializer<0:0>){0§<kotlin.Any>}[0]
+    open fun <#A1: kotlin/Any> polymorphicDefault(kotlin.reflect/KClass<#A1>, kotlin/Function1<kotlin/String?, kotlinx.serialization/DeserializationStrategy<#A1>?>) // kotlinx.serialization.modules/SerializersModuleCollector.polymorphicDefault|polymorphicDefault(kotlin.reflect.KClass<0:0>;kotlin.Function1<kotlin.String?,kotlinx.serialization.DeserializationStrategy<0:0>?>){0§<kotlin.Any>}[0]
+}
+
+abstract interface kotlinx.serialization/BinaryFormat : kotlinx.serialization/SerialFormat { // kotlinx.serialization/BinaryFormat|null[0]
+    abstract fun <#A1: kotlin/Any?> decodeFromByteArray(kotlinx.serialization/DeserializationStrategy<#A1>, kotlin/ByteArray): #A1 // kotlinx.serialization/BinaryFormat.decodeFromByteArray|decodeFromByteArray(kotlinx.serialization.DeserializationStrategy<0:0>;kotlin.ByteArray){0§<kotlin.Any?>}[0]
+    abstract fun <#A1: kotlin/Any?> encodeToByteArray(kotlinx.serialization/SerializationStrategy<#A1>, #A1): kotlin/ByteArray // kotlinx.serialization/BinaryFormat.encodeToByteArray|encodeToByteArray(kotlinx.serialization.SerializationStrategy<0:0>;0:0){0§<kotlin.Any?>}[0]
+}
+
+abstract interface kotlinx.serialization/SerialFormat { // kotlinx.serialization/SerialFormat|null[0]
+    abstract val serializersModule // kotlinx.serialization/SerialFormat.serializersModule|{}serializersModule[0]
+        abstract fun <get-serializersModule>(): kotlinx.serialization.modules/SerializersModule // kotlinx.serialization/SerialFormat.serializersModule.<get-serializersModule>|<get-serializersModule>(){}[0]
+}
+
+abstract interface kotlinx.serialization/StringFormat : kotlinx.serialization/SerialFormat { // kotlinx.serialization/StringFormat|null[0]
+    abstract fun <#A1: kotlin/Any?> decodeFromString(kotlinx.serialization/DeserializationStrategy<#A1>, kotlin/String): #A1 // kotlinx.serialization/StringFormat.decodeFromString|decodeFromString(kotlinx.serialization.DeserializationStrategy<0:0>;kotlin.String){0§<kotlin.Any?>}[0]
+    abstract fun <#A1: kotlin/Any?> encodeToString(kotlinx.serialization/SerializationStrategy<#A1>, #A1): kotlin/String // kotlinx.serialization/StringFormat.encodeToString|encodeToString(kotlinx.serialization.SerializationStrategy<0:0>;0:0){0§<kotlin.Any?>}[0]
+}
+
+abstract class <#A: kotlin/Any> kotlinx.serialization.internal/AbstractPolymorphicSerializer : kotlinx.serialization/KSerializer<#A> { // kotlinx.serialization.internal/AbstractPolymorphicSerializer|null[0]
+    abstract val baseClass // kotlinx.serialization.internal/AbstractPolymorphicSerializer.baseClass|{}baseClass[0]
+        abstract fun <get-baseClass>(): kotlin.reflect/KClass<#A> // kotlinx.serialization.internal/AbstractPolymorphicSerializer.baseClass.<get-baseClass>|<get-baseClass>(){}[0]
+
+    final fun deserialize(kotlinx.serialization.encoding/Decoder): #A // kotlinx.serialization.internal/AbstractPolymorphicSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
+    final fun serialize(kotlinx.serialization.encoding/Encoder, #A) // kotlinx.serialization.internal/AbstractPolymorphicSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;1:0){}[0]
+    open fun findPolymorphicSerializerOrNull(kotlinx.serialization.encoding/CompositeDecoder, kotlin/String?): kotlinx.serialization/DeserializationStrategy<#A>? // kotlinx.serialization.internal/AbstractPolymorphicSerializer.findPolymorphicSerializerOrNull|findPolymorphicSerializerOrNull(kotlinx.serialization.encoding.CompositeDecoder;kotlin.String?){}[0]
+    open fun findPolymorphicSerializerOrNull(kotlinx.serialization.encoding/Encoder, #A): kotlinx.serialization/SerializationStrategy<#A>? // kotlinx.serialization.internal/AbstractPolymorphicSerializer.findPolymorphicSerializerOrNull|findPolymorphicSerializerOrNull(kotlinx.serialization.encoding.Encoder;1:0){}[0]
+}
+
+abstract class <#A: kotlin/Any?, #B: kotlin.collections/Collection<#A>, #C: kotlin/Any?> kotlinx.serialization.internal/CollectionSerializer : kotlinx.serialization.internal/CollectionLikeSerializer<#A, #B, #C> { // kotlinx.serialization.internal/CollectionSerializer|null[0]
+    constructor <init>(kotlinx.serialization/KSerializer<#A>) // kotlinx.serialization.internal/CollectionSerializer.<init>|<init>(kotlinx.serialization.KSerializer<1:0>){}[0]
+
+    open fun (#B).collectionIterator(): kotlin.collections/Iterator<#A> // kotlinx.serialization.internal/CollectionSerializer.collectionIterator|collectionIterator@1:1(){}[0]
+    open fun (#B).collectionSize(): kotlin/Int // kotlinx.serialization.internal/CollectionSerializer.collectionSize|collectionSize@1:1(){}[0]
+}
+
+abstract class <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlinx.serialization.internal/PrimitiveArrayBuilder<#B>> kotlinx.serialization.internal/PrimitiveArraySerializer : kotlinx.serialization.internal/CollectionLikeSerializer<#A, #B, #C> { // kotlinx.serialization.internal/PrimitiveArraySerializer|null[0]
+    final val descriptor // kotlinx.serialization.internal/PrimitiveArraySerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.internal/PrimitiveArraySerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+
+    abstract fun empty(): #B // kotlinx.serialization.internal/PrimitiveArraySerializer.empty|empty(){}[0]
+    abstract fun readElement(kotlinx.serialization.encoding/CompositeDecoder, kotlin/Int, #C, kotlin/Boolean) // kotlinx.serialization.internal/PrimitiveArraySerializer.readElement|readElement(kotlinx.serialization.encoding.CompositeDecoder;kotlin.Int;1:2;kotlin.Boolean){}[0]
+    abstract fun writeContent(kotlinx.serialization.encoding/CompositeEncoder, #B, kotlin/Int) // kotlinx.serialization.internal/PrimitiveArraySerializer.writeContent|writeContent(kotlinx.serialization.encoding.CompositeEncoder;1:1;kotlin.Int){}[0]
+    final fun (#B).collectionIterator(): kotlin.collections/Iterator<#A> // kotlinx.serialization.internal/PrimitiveArraySerializer.collectionIterator|collectionIterator@1:1(){}[0]
+    final fun (#C).builderSize(): kotlin/Int // kotlinx.serialization.internal/PrimitiveArraySerializer.builderSize|builderSize@1:2(){}[0]
+    final fun (#C).checkCapacity(kotlin/Int) // kotlinx.serialization.internal/PrimitiveArraySerializer.checkCapacity|checkCapacity@1:2(kotlin.Int){}[0]
+    final fun (#C).insert(kotlin/Int, #A) // kotlinx.serialization.internal/PrimitiveArraySerializer.insert|insert@1:2(kotlin.Int;1:0){}[0]
+    final fun (#C).toResult(): #B // kotlinx.serialization.internal/PrimitiveArraySerializer.toResult|toResult@1:2(){}[0]
+    final fun builder(): #C // kotlinx.serialization.internal/PrimitiveArraySerializer.builder|builder(){}[0]
+    final fun deserialize(kotlinx.serialization.encoding/Decoder): #B // kotlinx.serialization.internal/PrimitiveArraySerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
+    final fun serialize(kotlinx.serialization.encoding/Encoder, #B) // kotlinx.serialization.internal/PrimitiveArraySerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;1:1){}[0]
+}
+
+abstract class <#A: kotlin/Any?> kotlinx.serialization.internal/PrimitiveArrayBuilder // kotlinx.serialization.internal/PrimitiveArrayBuilder|null[0]
+
+abstract class <#A: kotlin/Any?> kotlinx.serialization.internal/TaggedDecoder : kotlinx.serialization.encoding/CompositeDecoder, kotlinx.serialization.encoding/Decoder { // kotlinx.serialization.internal/TaggedDecoder|null[0]
+    constructor <init>() // kotlinx.serialization.internal/TaggedDecoder.<init>|<init>(){}[0]
+
+    final val currentTag // kotlinx.serialization.internal/TaggedDecoder.currentTag|{}currentTag[0]
+        final fun <get-currentTag>(): #A // kotlinx.serialization.internal/TaggedDecoder.currentTag.<get-currentTag>|<get-currentTag>(){}[0]
+    final val currentTagOrNull // kotlinx.serialization.internal/TaggedDecoder.currentTagOrNull|{}currentTagOrNull[0]
+        final fun <get-currentTagOrNull>(): #A? // kotlinx.serialization.internal/TaggedDecoder.currentTagOrNull.<get-currentTagOrNull>|<get-currentTagOrNull>(){}[0]
+    open val serializersModule // kotlinx.serialization.internal/TaggedDecoder.serializersModule|{}serializersModule[0]
+        open fun <get-serializersModule>(): kotlinx.serialization.modules/SerializersModule // kotlinx.serialization.internal/TaggedDecoder.serializersModule.<get-serializersModule>|<get-serializersModule>(){}[0]
+
+    abstract fun (kotlinx.serialization.descriptors/SerialDescriptor).getTag(kotlin/Int): #A // kotlinx.serialization.internal/TaggedDecoder.getTag|[email protected](kotlin.Int){}[0]
+    final fun <#A1: kotlin/Any> decodeNullableSerializableElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlinx.serialization/DeserializationStrategy<#A1?>, #A1?): #A1? // kotlinx.serialization.internal/TaggedDecoder.decodeNullableSerializableElement|decodeNullableSerializableElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlinx.serialization.DeserializationStrategy<0:0?>;0:0?){0§<kotlin.Any>}[0]
+    final fun <#A1: kotlin/Any?> decodeSerializableElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlinx.serialization/DeserializationStrategy<#A1>, #A1?): #A1 // kotlinx.serialization.internal/TaggedDecoder.decodeSerializableElement|decodeSerializableElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlinx.serialization.DeserializationStrategy<0:0>;0:0?){0§<kotlin.Any?>}[0]
+    final fun copyTagsTo(kotlinx.serialization.internal/TaggedDecoder<#A>) // kotlinx.serialization.internal/TaggedDecoder.copyTagsTo|copyTagsTo(kotlinx.serialization.internal.TaggedDecoder<1:0>){}[0]
+    final fun decodeBoolean(): kotlin/Boolean // kotlinx.serialization.internal/TaggedDecoder.decodeBoolean|decodeBoolean(){}[0]
+    final fun decodeBooleanElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Boolean // kotlinx.serialization.internal/TaggedDecoder.decodeBooleanElement|decodeBooleanElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+    final fun decodeByte(): kotlin/Byte // kotlinx.serialization.internal/TaggedDecoder.decodeByte|decodeByte(){}[0]
+    final fun decodeByteElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Byte // kotlinx.serialization.internal/TaggedDecoder.decodeByteElement|decodeByteElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+    final fun decodeChar(): kotlin/Char // kotlinx.serialization.internal/TaggedDecoder.decodeChar|decodeChar(){}[0]
+    final fun decodeCharElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Char // kotlinx.serialization.internal/TaggedDecoder.decodeCharElement|decodeCharElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+    final fun decodeDouble(): kotlin/Double // kotlinx.serialization.internal/TaggedDecoder.decodeDouble|decodeDouble(){}[0]
+    final fun decodeDoubleElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Double // kotlinx.serialization.internal/TaggedDecoder.decodeDoubleElement|decodeDoubleElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+    final fun decodeEnum(kotlinx.serialization.descriptors/SerialDescriptor): kotlin/Int // kotlinx.serialization.internal/TaggedDecoder.decodeEnum|decodeEnum(kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+    final fun decodeFloat(): kotlin/Float // kotlinx.serialization.internal/TaggedDecoder.decodeFloat|decodeFloat(){}[0]
+    final fun decodeFloatElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Float // kotlinx.serialization.internal/TaggedDecoder.decodeFloatElement|decodeFloatElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+    final fun decodeInlineElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlinx.serialization.encoding/Decoder // kotlinx.serialization.internal/TaggedDecoder.decodeInlineElement|decodeInlineElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+    final fun decodeInt(): kotlin/Int // kotlinx.serialization.internal/TaggedDecoder.decodeInt|decodeInt(){}[0]
+    final fun decodeIntElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Int // kotlinx.serialization.internal/TaggedDecoder.decodeIntElement|decodeIntElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+    final fun decodeLong(): kotlin/Long // kotlinx.serialization.internal/TaggedDecoder.decodeLong|decodeLong(){}[0]
+    final fun decodeLongElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Long // kotlinx.serialization.internal/TaggedDecoder.decodeLongElement|decodeLongElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+    final fun decodeNull(): kotlin/Nothing? // kotlinx.serialization.internal/TaggedDecoder.decodeNull|decodeNull(){}[0]
+    final fun decodeShort(): kotlin/Short // kotlinx.serialization.internal/TaggedDecoder.decodeShort|decodeShort(){}[0]
+    final fun decodeShortElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Short // kotlinx.serialization.internal/TaggedDecoder.decodeShortElement|decodeShortElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+    final fun decodeString(): kotlin/String // kotlinx.serialization.internal/TaggedDecoder.decodeString|decodeString(){}[0]
+    final fun decodeStringElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/String // kotlinx.serialization.internal/TaggedDecoder.decodeStringElement|decodeStringElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+    final fun popTag(): #A // kotlinx.serialization.internal/TaggedDecoder.popTag|popTag(){}[0]
+    final fun pushTag(#A) // kotlinx.serialization.internal/TaggedDecoder.pushTag|pushTag(1:0){}[0]
+    open fun <#A1: kotlin/Any?> decodeSerializableValue(kotlinx.serialization/DeserializationStrategy<#A1>, #A1?): #A1 // kotlinx.serialization.internal/TaggedDecoder.decodeSerializableValue|decodeSerializableValue(kotlinx.serialization.DeserializationStrategy<0:0>;0:0?){0§<kotlin.Any?>}[0]
+    open fun beginStructure(kotlinx.serialization.descriptors/SerialDescriptor): kotlinx.serialization.encoding/CompositeDecoder // kotlinx.serialization.internal/TaggedDecoder.beginStructure|beginStructure(kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+    open fun decodeInline(kotlinx.serialization.descriptors/SerialDescriptor): kotlinx.serialization.encoding/Decoder // kotlinx.serialization.internal/TaggedDecoder.decodeInline|decodeInline(kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+    open fun decodeNotNullMark(): kotlin/Boolean // kotlinx.serialization.internal/TaggedDecoder.decodeNotNullMark|decodeNotNullMark(){}[0]
+    open fun decodeTaggedBoolean(#A): kotlin/Boolean // kotlinx.serialization.internal/TaggedDecoder.decodeTaggedBoolean|decodeTaggedBoolean(1:0){}[0]
+    open fun decodeTaggedByte(#A): kotlin/Byte // kotlinx.serialization.internal/TaggedDecoder.decodeTaggedByte|decodeTaggedByte(1:0){}[0]
+    open fun decodeTaggedChar(#A): kotlin/Char // kotlinx.serialization.internal/TaggedDecoder.decodeTaggedChar|decodeTaggedChar(1:0){}[0]
+    open fun decodeTaggedDouble(#A): kotlin/Double // kotlinx.serialization.internal/TaggedDecoder.decodeTaggedDouble|decodeTaggedDouble(1:0){}[0]
+    open fun decodeTaggedEnum(#A, kotlinx.serialization.descriptors/SerialDescriptor): kotlin/Int // kotlinx.serialization.internal/TaggedDecoder.decodeTaggedEnum|decodeTaggedEnum(1:0;kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+    open fun decodeTaggedFloat(#A): kotlin/Float // kotlinx.serialization.internal/TaggedDecoder.decodeTaggedFloat|decodeTaggedFloat(1:0){}[0]
+    open fun decodeTaggedInline(#A, kotlinx.serialization.descriptors/SerialDescriptor): kotlinx.serialization.encoding/Decoder // kotlinx.serialization.internal/TaggedDecoder.decodeTaggedInline|decodeTaggedInline(1:0;kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+    open fun decodeTaggedInt(#A): kotlin/Int // kotlinx.serialization.internal/TaggedDecoder.decodeTaggedInt|decodeTaggedInt(1:0){}[0]
+    open fun decodeTaggedLong(#A): kotlin/Long // kotlinx.serialization.internal/TaggedDecoder.decodeTaggedLong|decodeTaggedLong(1:0){}[0]
+    open fun decodeTaggedNotNullMark(#A): kotlin/Boolean // kotlinx.serialization.internal/TaggedDecoder.decodeTaggedNotNullMark|decodeTaggedNotNullMark(1:0){}[0]
+    open fun decodeTaggedNull(#A): kotlin/Nothing? // kotlinx.serialization.internal/TaggedDecoder.decodeTaggedNull|decodeTaggedNull(1:0){}[0]
+    open fun decodeTaggedShort(#A): kotlin/Short // kotlinx.serialization.internal/TaggedDecoder.decodeTaggedShort|decodeTaggedShort(1:0){}[0]
+    open fun decodeTaggedString(#A): kotlin/String // kotlinx.serialization.internal/TaggedDecoder.decodeTaggedString|decodeTaggedString(1:0){}[0]
+    open fun decodeTaggedValue(#A): kotlin/Any // kotlinx.serialization.internal/TaggedDecoder.decodeTaggedValue|decodeTaggedValue(1:0){}[0]
+    open fun endStructure(kotlinx.serialization.descriptors/SerialDescriptor) // kotlinx.serialization.internal/TaggedDecoder.endStructure|endStructure(kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+}
+
+abstract class <#A: kotlin/Any?> kotlinx.serialization.internal/TaggedEncoder : kotlinx.serialization.encoding/CompositeEncoder, kotlinx.serialization.encoding/Encoder { // kotlinx.serialization.internal/TaggedEncoder|null[0]
+    constructor <init>() // kotlinx.serialization.internal/TaggedEncoder.<init>|<init>(){}[0]
+
+    final val currentTag // kotlinx.serialization.internal/TaggedEncoder.currentTag|{}currentTag[0]
+        final fun <get-currentTag>(): #A // kotlinx.serialization.internal/TaggedEncoder.currentTag.<get-currentTag>|<get-currentTag>(){}[0]
+    final val currentTagOrNull // kotlinx.serialization.internal/TaggedEncoder.currentTagOrNull|{}currentTagOrNull[0]
+        final fun <get-currentTagOrNull>(): #A? // kotlinx.serialization.internal/TaggedEncoder.currentTagOrNull.<get-currentTagOrNull>|<get-currentTagOrNull>(){}[0]
+    open val serializersModule // kotlinx.serialization.internal/TaggedEncoder.serializersModule|{}serializersModule[0]
+        open fun <get-serializersModule>(): kotlinx.serialization.modules/SerializersModule // kotlinx.serialization.internal/TaggedEncoder.serializersModule.<get-serializersModule>|<get-serializersModule>(){}[0]
+
+    abstract fun (kotlinx.serialization.descriptors/SerialDescriptor).getTag(kotlin/Int): #A // kotlinx.serialization.internal/TaggedEncoder.getTag|[email protected](kotlin.Int){}[0]
+    final fun encodeBoolean(kotlin/Boolean) // kotlinx.serialization.internal/TaggedEncoder.encodeBoolean|encodeBoolean(kotlin.Boolean){}[0]
+    final fun encodeBooleanElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Boolean) // kotlinx.serialization.internal/TaggedEncoder.encodeBooleanElement|encodeBooleanElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Boolean){}[0]
+    final fun encodeByte(kotlin/Byte) // kotlinx.serialization.internal/TaggedEncoder.encodeByte|encodeByte(kotlin.Byte){}[0]
+    final fun encodeByteElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Byte) // kotlinx.serialization.internal/TaggedEncoder.encodeByteElement|encodeByteElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Byte){}[0]
+    final fun encodeChar(kotlin/Char) // kotlinx.serialization.internal/TaggedEncoder.encodeChar|encodeChar(kotlin.Char){}[0]
+    final fun encodeCharElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Char) // kotlinx.serialization.internal/TaggedEncoder.encodeCharElement|encodeCharElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Char){}[0]
+    final fun encodeDouble(kotlin/Double) // kotlinx.serialization.internal/TaggedEncoder.encodeDouble|encodeDouble(kotlin.Double){}[0]
+    final fun encodeDoubleElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Double) // kotlinx.serialization.internal/TaggedEncoder.encodeDoubleElement|encodeDoubleElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Double){}[0]
+    final fun encodeEnum(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int) // kotlinx.serialization.internal/TaggedEncoder.encodeEnum|encodeEnum(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+    final fun encodeFloat(kotlin/Float) // kotlinx.serialization.internal/TaggedEncoder.encodeFloat|encodeFloat(kotlin.Float){}[0]
+    final fun encodeFloatElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Float) // kotlinx.serialization.internal/TaggedEncoder.encodeFloatElement|encodeFloatElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Float){}[0]
+    final fun encodeInlineElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlinx.serialization.encoding/Encoder // kotlinx.serialization.internal/TaggedEncoder.encodeInlineElement|encodeInlineElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+    final fun encodeInt(kotlin/Int) // kotlinx.serialization.internal/TaggedEncoder.encodeInt|encodeInt(kotlin.Int){}[0]
+    final fun encodeIntElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Int) // kotlinx.serialization.internal/TaggedEncoder.encodeIntElement|encodeIntElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Int){}[0]
+    final fun encodeLong(kotlin/Long) // kotlinx.serialization.internal/TaggedEncoder.encodeLong|encodeLong(kotlin.Long){}[0]
+    final fun encodeLongElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Long) // kotlinx.serialization.internal/TaggedEncoder.encodeLongElement|encodeLongElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Long){}[0]
+    final fun encodeShort(kotlin/Short) // kotlinx.serialization.internal/TaggedEncoder.encodeShort|encodeShort(kotlin.Short){}[0]
+    final fun encodeShortElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Short) // kotlinx.serialization.internal/TaggedEncoder.encodeShortElement|encodeShortElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Short){}[0]
+    final fun encodeString(kotlin/String) // kotlinx.serialization.internal/TaggedEncoder.encodeString|encodeString(kotlin.String){}[0]
+    final fun encodeStringElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/String) // kotlinx.serialization.internal/TaggedEncoder.encodeStringElement|encodeStringElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.String){}[0]
+    final fun endStructure(kotlinx.serialization.descriptors/SerialDescriptor) // kotlinx.serialization.internal/TaggedEncoder.endStructure|endStructure(kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+    final fun popTag(): #A // kotlinx.serialization.internal/TaggedEncoder.popTag|popTag(){}[0]
+    final fun pushTag(#A) // kotlinx.serialization.internal/TaggedEncoder.pushTag|pushTag(1:0){}[0]
+    open fun <#A1: kotlin/Any> encodeNullableSerializableElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlinx.serialization/SerializationStrategy<#A1>, #A1?) // kotlinx.serialization.internal/TaggedEncoder.encodeNullableSerializableElement|encodeNullableSerializableElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlinx.serialization.SerializationStrategy<0:0>;0:0?){0§<kotlin.Any>}[0]
+    open fun <#A1: kotlin/Any?> encodeSerializableElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlinx.serialization/SerializationStrategy<#A1>, #A1) // kotlinx.serialization.internal/TaggedEncoder.encodeSerializableElement|encodeSerializableElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlinx.serialization.SerializationStrategy<0:0>;0:0){0§<kotlin.Any?>}[0]
+    open fun beginStructure(kotlinx.serialization.descriptors/SerialDescriptor): kotlinx.serialization.encoding/CompositeEncoder // kotlinx.serialization.internal/TaggedEncoder.beginStructure|beginStructure(kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+    open fun encodeInline(kotlinx.serialization.descriptors/SerialDescriptor): kotlinx.serialization.encoding/Encoder // kotlinx.serialization.internal/TaggedEncoder.encodeInline|encodeInline(kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+    open fun encodeNotNullMark() // kotlinx.serialization.internal/TaggedEncoder.encodeNotNullMark|encodeNotNullMark(){}[0]
+    open fun encodeNull() // kotlinx.serialization.internal/TaggedEncoder.encodeNull|encodeNull(){}[0]
+    open fun encodeTaggedBoolean(#A, kotlin/Boolean) // kotlinx.serialization.internal/TaggedEncoder.encodeTaggedBoolean|encodeTaggedBoolean(1:0;kotlin.Boolean){}[0]
+    open fun encodeTaggedByte(#A, kotlin/Byte) // kotlinx.serialization.internal/TaggedEncoder.encodeTaggedByte|encodeTaggedByte(1:0;kotlin.Byte){}[0]
+    open fun encodeTaggedChar(#A, kotlin/Char) // kotlinx.serialization.internal/TaggedEncoder.encodeTaggedChar|encodeTaggedChar(1:0;kotlin.Char){}[0]
+    open fun encodeTaggedDouble(#A, kotlin/Double) // kotlinx.serialization.internal/TaggedEncoder.encodeTaggedDouble|encodeTaggedDouble(1:0;kotlin.Double){}[0]
+    open fun encodeTaggedEnum(#A, kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int) // kotlinx.serialization.internal/TaggedEncoder.encodeTaggedEnum|encodeTaggedEnum(1:0;kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+    open fun encodeTaggedFloat(#A, kotlin/Float) // kotlinx.serialization.internal/TaggedEncoder.encodeTaggedFloat|encodeTaggedFloat(1:0;kotlin.Float){}[0]
+    open fun encodeTaggedInline(#A, kotlinx.serialization.descriptors/SerialDescriptor): kotlinx.serialization.encoding/Encoder // kotlinx.serialization.internal/TaggedEncoder.encodeTaggedInline|encodeTaggedInline(1:0;kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+    open fun encodeTaggedInt(#A, kotlin/Int) // kotlinx.serialization.internal/TaggedEncoder.encodeTaggedInt|encodeTaggedInt(1:0;kotlin.Int){}[0]
+    open fun encodeTaggedLong(#A, kotlin/Long) // kotlinx.serialization.internal/TaggedEncoder.encodeTaggedLong|encodeTaggedLong(1:0;kotlin.Long){}[0]
+    open fun encodeTaggedNonNullMark(#A) // kotlinx.serialization.internal/TaggedEncoder.encodeTaggedNonNullMark|encodeTaggedNonNullMark(1:0){}[0]
+    open fun encodeTaggedNull(#A) // kotlinx.serialization.internal/TaggedEncoder.encodeTaggedNull|encodeTaggedNull(1:0){}[0]
+    open fun encodeTaggedShort(#A, kotlin/Short) // kotlinx.serialization.internal/TaggedEncoder.encodeTaggedShort|encodeTaggedShort(1:0;kotlin.Short){}[0]
+    open fun encodeTaggedString(#A, kotlin/String) // kotlinx.serialization.internal/TaggedEncoder.encodeTaggedString|encodeTaggedString(1:0;kotlin.String){}[0]
+    open fun encodeTaggedValue(#A, kotlin/Any) // kotlinx.serialization.internal/TaggedEncoder.encodeTaggedValue|encodeTaggedValue(1:0;kotlin.Any){}[0]
+    open fun endEncode(kotlinx.serialization.descriptors/SerialDescriptor) // kotlinx.serialization.internal/TaggedEncoder.endEncode|endEncode(kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+}
+
+abstract class kotlinx.serialization.encoding/AbstractDecoder : kotlinx.serialization.encoding/CompositeDecoder, kotlinx.serialization.encoding/Decoder { // kotlinx.serialization.encoding/AbstractDecoder|null[0]
+    constructor <init>() // kotlinx.serialization.encoding/AbstractDecoder.<init>|<init>(){}[0]
+
+    final fun <#A1: kotlin/Any> decodeNullableSerializableElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlinx.serialization/DeserializationStrategy<#A1?>, #A1?): #A1? // kotlinx.serialization.encoding/AbstractDecoder.decodeNullableSerializableElement|decodeNullableSerializableElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlinx.serialization.DeserializationStrategy<0:0?>;0:0?){0§<kotlin.Any>}[0]
+    final fun decodeBooleanElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Boolean // kotlinx.serialization.encoding/AbstractDecoder.decodeBooleanElement|decodeBooleanElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+    final fun decodeByteElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Byte // kotlinx.serialization.encoding/AbstractDecoder.decodeByteElement|decodeByteElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+    final fun decodeCharElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Char // kotlinx.serialization.encoding/AbstractDecoder.decodeCharElement|decodeCharElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+    final fun decodeDoubleElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Double // kotlinx.serialization.encoding/AbstractDecoder.decodeDoubleElement|decodeDoubleElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+    final fun decodeFloatElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Float // kotlinx.serialization.encoding/AbstractDecoder.decodeFloatElement|decodeFloatElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+    final fun decodeIntElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Int // kotlinx.serialization.encoding/AbstractDecoder.decodeIntElement|decodeIntElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+    final fun decodeLongElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Long // kotlinx.serialization.encoding/AbstractDecoder.decodeLongElement|decodeLongElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+    final fun decodeShortElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Short // kotlinx.serialization.encoding/AbstractDecoder.decodeShortElement|decodeShortElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+    final fun decodeStringElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/String // kotlinx.serialization.encoding/AbstractDecoder.decodeStringElement|decodeStringElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+    open fun <#A1: kotlin/Any?> decodeSerializableElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlinx.serialization/DeserializationStrategy<#A1>, #A1?): #A1 // kotlinx.serialization.encoding/AbstractDecoder.decodeSerializableElement|decodeSerializableElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlinx.serialization.DeserializationStrategy<0:0>;0:0?){0§<kotlin.Any?>}[0]
+    open fun <#A1: kotlin/Any?> decodeSerializableValue(kotlinx.serialization/DeserializationStrategy<#A1>, #A1? = ...): #A1 // kotlinx.serialization.encoding/AbstractDecoder.decodeSerializableValue|decodeSerializableValue(kotlinx.serialization.DeserializationStrategy<0:0>;0:0?){0§<kotlin.Any?>}[0]
+    open fun beginStructure(kotlinx.serialization.descriptors/SerialDescriptor): kotlinx.serialization.encoding/CompositeDecoder // kotlinx.serialization.encoding/AbstractDecoder.beginStructure|beginStructure(kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+    open fun decodeBoolean(): kotlin/Boolean // kotlinx.serialization.encoding/AbstractDecoder.decodeBoolean|decodeBoolean(){}[0]
+    open fun decodeByte(): kotlin/Byte // kotlinx.serialization.encoding/AbstractDecoder.decodeByte|decodeByte(){}[0]
+    open fun decodeChar(): kotlin/Char // kotlinx.serialization.encoding/AbstractDecoder.decodeChar|decodeChar(){}[0]
+    open fun decodeDouble(): kotlin/Double // kotlinx.serialization.encoding/AbstractDecoder.decodeDouble|decodeDouble(){}[0]
+    open fun decodeEnum(kotlinx.serialization.descriptors/SerialDescriptor): kotlin/Int // kotlinx.serialization.encoding/AbstractDecoder.decodeEnum|decodeEnum(kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+    open fun decodeFloat(): kotlin/Float // kotlinx.serialization.encoding/AbstractDecoder.decodeFloat|decodeFloat(){}[0]
+    open fun decodeInline(kotlinx.serialization.descriptors/SerialDescriptor): kotlinx.serialization.encoding/Decoder // kotlinx.serialization.encoding/AbstractDecoder.decodeInline|decodeInline(kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+    open fun decodeInlineElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlinx.serialization.encoding/Decoder // kotlinx.serialization.encoding/AbstractDecoder.decodeInlineElement|decodeInlineElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+    open fun decodeInt(): kotlin/Int // kotlinx.serialization.encoding/AbstractDecoder.decodeInt|decodeInt(){}[0]
+    open fun decodeLong(): kotlin/Long // kotlinx.serialization.encoding/AbstractDecoder.decodeLong|decodeLong(){}[0]
+    open fun decodeNotNullMark(): kotlin/Boolean // kotlinx.serialization.encoding/AbstractDecoder.decodeNotNullMark|decodeNotNullMark(){}[0]
+    open fun decodeNull(): kotlin/Nothing? // kotlinx.serialization.encoding/AbstractDecoder.decodeNull|decodeNull(){}[0]
+    open fun decodeShort(): kotlin/Short // kotlinx.serialization.encoding/AbstractDecoder.decodeShort|decodeShort(){}[0]
+    open fun decodeString(): kotlin/String // kotlinx.serialization.encoding/AbstractDecoder.decodeString|decodeString(){}[0]
+    open fun decodeValue(): kotlin/Any // kotlinx.serialization.encoding/AbstractDecoder.decodeValue|decodeValue(){}[0]
+    open fun endStructure(kotlinx.serialization.descriptors/SerialDescriptor) // kotlinx.serialization.encoding/AbstractDecoder.endStructure|endStructure(kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+}
+
+abstract class kotlinx.serialization.encoding/AbstractEncoder : kotlinx.serialization.encoding/CompositeEncoder, kotlinx.serialization.encoding/Encoder { // kotlinx.serialization.encoding/AbstractEncoder|null[0]
+    constructor <init>() // kotlinx.serialization.encoding/AbstractEncoder.<init>|<init>(){}[0]
+
+    final fun encodeBooleanElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Boolean) // kotlinx.serialization.encoding/AbstractEncoder.encodeBooleanElement|encodeBooleanElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Boolean){}[0]
+    final fun encodeByteElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Byte) // kotlinx.serialization.encoding/AbstractEncoder.encodeByteElement|encodeByteElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Byte){}[0]
+    final fun encodeCharElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Char) // kotlinx.serialization.encoding/AbstractEncoder.encodeCharElement|encodeCharElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Char){}[0]
+    final fun encodeDoubleElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Double) // kotlinx.serialization.encoding/AbstractEncoder.encodeDoubleElement|encodeDoubleElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Double){}[0]
+    final fun encodeFloatElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Float) // kotlinx.serialization.encoding/AbstractEncoder.encodeFloatElement|encodeFloatElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Float){}[0]
+    final fun encodeInlineElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlinx.serialization.encoding/Encoder // kotlinx.serialization.encoding/AbstractEncoder.encodeInlineElement|encodeInlineElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+    final fun encodeIntElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Int) // kotlinx.serialization.encoding/AbstractEncoder.encodeIntElement|encodeIntElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Int){}[0]
+    final fun encodeLongElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Long) // kotlinx.serialization.encoding/AbstractEncoder.encodeLongElement|encodeLongElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Long){}[0]
+    final fun encodeShortElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Short) // kotlinx.serialization.encoding/AbstractEncoder.encodeShortElement|encodeShortElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Short){}[0]
+    final fun encodeStringElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/String) // kotlinx.serialization.encoding/AbstractEncoder.encodeStringElement|encodeStringElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.String){}[0]
+    open fun <#A1: kotlin/Any> encodeNullableSerializableElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlinx.serialization/SerializationStrategy<#A1>, #A1?) // kotlinx.serialization.encoding/AbstractEncoder.encodeNullableSerializableElement|encodeNullableSerializableElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlinx.serialization.SerializationStrategy<0:0>;0:0?){0§<kotlin.Any>}[0]
+    open fun <#A1: kotlin/Any?> encodeSerializableElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlinx.serialization/SerializationStrategy<#A1>, #A1) // kotlinx.serialization.encoding/AbstractEncoder.encodeSerializableElement|encodeSerializableElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlinx.serialization.SerializationStrategy<0:0>;0:0){0§<kotlin.Any?>}[0]
+    open fun beginStructure(kotlinx.serialization.descriptors/SerialDescriptor): kotlinx.serialization.encoding/CompositeEncoder // kotlinx.serialization.encoding/AbstractEncoder.beginStructure|beginStructure(kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+    open fun encodeBoolean(kotlin/Boolean) // kotlinx.serialization.encoding/AbstractEncoder.encodeBoolean|encodeBoolean(kotlin.Boolean){}[0]
+    open fun encodeByte(kotlin/Byte) // kotlinx.serialization.encoding/AbstractEncoder.encodeByte|encodeByte(kotlin.Byte){}[0]
+    open fun encodeChar(kotlin/Char) // kotlinx.serialization.encoding/AbstractEncoder.encodeChar|encodeChar(kotlin.Char){}[0]
+    open fun encodeDouble(kotlin/Double) // kotlinx.serialization.encoding/AbstractEncoder.encodeDouble|encodeDouble(kotlin.Double){}[0]
+    open fun encodeElement(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/Boolean // kotlinx.serialization.encoding/AbstractEncoder.encodeElement|encodeElement(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+    open fun encodeEnum(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int) // kotlinx.serialization.encoding/AbstractEncoder.encodeEnum|encodeEnum(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+    open fun encodeFloat(kotlin/Float) // kotlinx.serialization.encoding/AbstractEncoder.encodeFloat|encodeFloat(kotlin.Float){}[0]
+    open fun encodeInline(kotlinx.serialization.descriptors/SerialDescriptor): kotlinx.serialization.encoding/Encoder // kotlinx.serialization.encoding/AbstractEncoder.encodeInline|encodeInline(kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+    open fun encodeInt(kotlin/Int) // kotlinx.serialization.encoding/AbstractEncoder.encodeInt|encodeInt(kotlin.Int){}[0]
+    open fun encodeLong(kotlin/Long) // kotlinx.serialization.encoding/AbstractEncoder.encodeLong|encodeLong(kotlin.Long){}[0]
+    open fun encodeNull() // kotlinx.serialization.encoding/AbstractEncoder.encodeNull|encodeNull(){}[0]
+    open fun encodeShort(kotlin/Short) // kotlinx.serialization.encoding/AbstractEncoder.encodeShort|encodeShort(kotlin.Short){}[0]
+    open fun encodeString(kotlin/String) // kotlinx.serialization.encoding/AbstractEncoder.encodeString|encodeString(kotlin.String){}[0]
+    open fun encodeValue(kotlin/Any) // kotlinx.serialization.encoding/AbstractEncoder.encodeValue|encodeValue(kotlin.Any){}[0]
+    open fun endStructure(kotlinx.serialization.descriptors/SerialDescriptor) // kotlinx.serialization.encoding/AbstractEncoder.endStructure|endStructure(kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+}
+
+abstract class kotlinx.serialization.internal/NamedValueDecoder : kotlinx.serialization.internal/TaggedDecoder<kotlin/String> { // kotlinx.serialization.internal/NamedValueDecoder|null[0]
+    constructor <init>() // kotlinx.serialization.internal/NamedValueDecoder.<init>|<init>(){}[0]
+
+    final fun (kotlinx.serialization.descriptors/SerialDescriptor).getTag(kotlin/Int): kotlin/String // kotlinx.serialization.internal/NamedValueDecoder.getTag|[email protected](kotlin.Int){}[0]
+    final fun nested(kotlin/String): kotlin/String // kotlinx.serialization.internal/NamedValueDecoder.nested|nested(kotlin.String){}[0]
+    final fun renderTagStack(): kotlin/String // kotlinx.serialization.internal/NamedValueDecoder.renderTagStack|renderTagStack(){}[0]
+    open fun composeName(kotlin/String, kotlin/String): kotlin/String // kotlinx.serialization.internal/NamedValueDecoder.composeName|composeName(kotlin.String;kotlin.String){}[0]
+    open fun elementName(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/String // kotlinx.serialization.internal/NamedValueDecoder.elementName|elementName(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+}
+
+abstract class kotlinx.serialization.internal/NamedValueEncoder : kotlinx.serialization.internal/TaggedEncoder<kotlin/String> { // kotlinx.serialization.internal/NamedValueEncoder|null[0]
+    constructor <init>() // kotlinx.serialization.internal/NamedValueEncoder.<init>|<init>(){}[0]
+
+    final fun (kotlinx.serialization.descriptors/SerialDescriptor).getTag(kotlin/Int): kotlin/String // kotlinx.serialization.internal/NamedValueEncoder.getTag|[email protected](kotlin.Int){}[0]
+    final fun nested(kotlin/String): kotlin/String // kotlinx.serialization.internal/NamedValueEncoder.nested|nested(kotlin.String){}[0]
+    open fun composeName(kotlin/String, kotlin/String): kotlin/String // kotlinx.serialization.internal/NamedValueEncoder.composeName|composeName(kotlin.String;kotlin.String){}[0]
+    open fun elementName(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int): kotlin/String // kotlinx.serialization.internal/NamedValueEncoder.elementName|elementName(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int){}[0]
+}
+
+final class <#A: in kotlin/Any> kotlinx.serialization.modules/PolymorphicModuleBuilder { // kotlinx.serialization.modules/PolymorphicModuleBuilder|null[0]
+    constructor <init>(kotlin.reflect/KClass<#A>, kotlinx.serialization/KSerializer<#A>? = ...) // kotlinx.serialization.modules/PolymorphicModuleBuilder.<init>|<init>(kotlin.reflect.KClass<1:0>;kotlinx.serialization.KSerializer<1:0>?){}[0]
+
+    final fun <#A1: #A> subclass(kotlin.reflect/KClass<#A1>, kotlinx.serialization/KSerializer<#A1>) // kotlinx.serialization.modules/PolymorphicModuleBuilder.subclass|subclass(kotlin.reflect.KClass<0:0>;kotlinx.serialization.KSerializer<0:0>){0§<1:0>}[0]
+    final fun buildTo(kotlinx.serialization.modules/SerializersModuleBuilder) // kotlinx.serialization.modules/PolymorphicModuleBuilder.buildTo|buildTo(kotlinx.serialization.modules.SerializersModuleBuilder){}[0]
+    final fun default(kotlin/Function1<kotlin/String?, kotlinx.serialization/DeserializationStrategy<#A>?>) // kotlinx.serialization.modules/PolymorphicModuleBuilder.default|default(kotlin.Function1<kotlin.String?,kotlinx.serialization.DeserializationStrategy<1:0>?>){}[0]
+    final fun defaultDeserializer(kotlin/Function1<kotlin/String?, kotlinx.serialization/DeserializationStrategy<#A>?>) // kotlinx.serialization.modules/PolymorphicModuleBuilder.defaultDeserializer|defaultDeserializer(kotlin.Function1<kotlin.String?,kotlinx.serialization.DeserializationStrategy<1:0>?>){}[0]
+}
+
+final class <#A: kotlin/Any, #B: #A?> kotlinx.serialization.internal/ReferenceArraySerializer : kotlinx.serialization.internal/CollectionLikeSerializer<#B, kotlin/Array<#B>, kotlin.collections/ArrayList<#B>> { // kotlinx.serialization.internal/ReferenceArraySerializer|null[0]
+    constructor <init>(kotlin.reflect/KClass<#A>, kotlinx.serialization/KSerializer<#B>) // kotlinx.serialization.internal/ReferenceArraySerializer.<init>|<init>(kotlin.reflect.KClass<1:0>;kotlinx.serialization.KSerializer<1:1>){}[0]
+
+    final val descriptor // kotlinx.serialization.internal/ReferenceArraySerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.internal/ReferenceArraySerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+}
+
+final class <#A: kotlin/Any> kotlinx.serialization.internal/NullableSerializer : kotlinx.serialization/KSerializer<#A?> { // kotlinx.serialization.internal/NullableSerializer|null[0]
+    constructor <init>(kotlinx.serialization/KSerializer<#A>) // kotlinx.serialization.internal/NullableSerializer.<init>|<init>(kotlinx.serialization.KSerializer<1:0>){}[0]
+
+    final val descriptor // kotlinx.serialization.internal/NullableSerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.internal/NullableSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+
+    final fun deserialize(kotlinx.serialization.encoding/Decoder): #A? // kotlinx.serialization.internal/NullableSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
+    final fun equals(kotlin/Any?): kotlin/Boolean // kotlinx.serialization.internal/NullableSerializer.equals|equals(kotlin.Any?){}[0]
+    final fun hashCode(): kotlin/Int // kotlinx.serialization.internal/NullableSerializer.hashCode|hashCode(){}[0]
+    final fun serialize(kotlinx.serialization.encoding/Encoder, #A?) // kotlinx.serialization.internal/NullableSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;1:0?){}[0]
+}
+
+final class <#A: kotlin/Any> kotlinx.serialization.internal/ObjectSerializer : kotlinx.serialization/KSerializer<#A> { // kotlinx.serialization.internal/ObjectSerializer|null[0]
+    constructor <init>(kotlin/String, #A) // kotlinx.serialization.internal/ObjectSerializer.<init>|<init>(kotlin.String;1:0){}[0]
+    constructor <init>(kotlin/String, #A, kotlin/Array<kotlin/Annotation>) // kotlinx.serialization.internal/ObjectSerializer.<init>|<init>(kotlin.String;1:0;kotlin.Array<kotlin.Annotation>){}[0]
+
+    final val descriptor // kotlinx.serialization.internal/ObjectSerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.internal/ObjectSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+
+    final fun deserialize(kotlinx.serialization.encoding/Decoder): #A // kotlinx.serialization.internal/ObjectSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
+    final fun serialize(kotlinx.serialization.encoding/Encoder, #A) // kotlinx.serialization.internal/ObjectSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;1:0){}[0]
+}
+
+final class <#A: kotlin/Any> kotlinx.serialization/ContextualSerializer : kotlinx.serialization/KSerializer<#A> { // kotlinx.serialization/ContextualSerializer|null[0]
+    constructor <init>(kotlin.reflect/KClass<#A>) // kotlinx.serialization/ContextualSerializer.<init>|<init>(kotlin.reflect.KClass<1:0>){}[0]
+    constructor <init>(kotlin.reflect/KClass<#A>, kotlinx.serialization/KSerializer<#A>?, kotlin/Array<kotlinx.serialization/KSerializer<*>>) // kotlinx.serialization/ContextualSerializer.<init>|<init>(kotlin.reflect.KClass<1:0>;kotlinx.serialization.KSerializer<1:0>?;kotlin.Array<kotlinx.serialization.KSerializer<*>>){}[0]
+
+    final val descriptor // kotlinx.serialization/ContextualSerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization/ContextualSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+
+    final fun deserialize(kotlinx.serialization.encoding/Decoder): #A // kotlinx.serialization/ContextualSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
+    final fun serialize(kotlinx.serialization.encoding/Encoder, #A) // kotlinx.serialization/ContextualSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;1:0){}[0]
+}
+
+final class <#A: kotlin/Any> kotlinx.serialization/PolymorphicSerializer : kotlinx.serialization.internal/AbstractPolymorphicSerializer<#A> { // kotlinx.serialization/PolymorphicSerializer|null[0]
+    constructor <init>(kotlin.reflect/KClass<#A>) // kotlinx.serialization/PolymorphicSerializer.<init>|<init>(kotlin.reflect.KClass<1:0>){}[0]
+    constructor <init>(kotlin.reflect/KClass<#A>, kotlin/Array<kotlin/Annotation>) // kotlinx.serialization/PolymorphicSerializer.<init>|<init>(kotlin.reflect.KClass<1:0>;kotlin.Array<kotlin.Annotation>){}[0]
+
+    final val baseClass // kotlinx.serialization/PolymorphicSerializer.baseClass|{}baseClass[0]
+        final fun <get-baseClass>(): kotlin.reflect/KClass<#A> // kotlinx.serialization/PolymorphicSerializer.baseClass.<get-baseClass>|<get-baseClass>(){}[0]
+    final val descriptor // kotlinx.serialization/PolymorphicSerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization/PolymorphicSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+
+    final fun toString(): kotlin/String // kotlinx.serialization/PolymorphicSerializer.toString|toString(){}[0]
+}
+
+final class <#A: kotlin/Any> kotlinx.serialization/SealedClassSerializer : kotlinx.serialization.internal/AbstractPolymorphicSerializer<#A> { // kotlinx.serialization/SealedClassSerializer|null[0]
+    constructor <init>(kotlin/String, kotlin.reflect/KClass<#A>, kotlin/Array<kotlin.reflect/KClass<out #A>>, kotlin/Array<kotlinx.serialization/KSerializer<out #A>>) // kotlinx.serialization/SealedClassSerializer.<init>|<init>(kotlin.String;kotlin.reflect.KClass<1:0>;kotlin.Array<kotlin.reflect.KClass<out|1:0>>;kotlin.Array<kotlinx.serialization.KSerializer<out|1:0>>){}[0]
+    constructor <init>(kotlin/String, kotlin.reflect/KClass<#A>, kotlin/Array<kotlin.reflect/KClass<out #A>>, kotlin/Array<kotlinx.serialization/KSerializer<out #A>>, kotlin/Array<kotlin/Annotation>) // kotlinx.serialization/SealedClassSerializer.<init>|<init>(kotlin.String;kotlin.reflect.KClass<1:0>;kotlin.Array<kotlin.reflect.KClass<out|1:0>>;kotlin.Array<kotlinx.serialization.KSerializer<out|1:0>>;kotlin.Array<kotlin.Annotation>){}[0]
+
+    final val baseClass // kotlinx.serialization/SealedClassSerializer.baseClass|{}baseClass[0]
+        final fun <get-baseClass>(): kotlin.reflect/KClass<#A> // kotlinx.serialization/SealedClassSerializer.baseClass.<get-baseClass>|<get-baseClass>(){}[0]
+    final val descriptor // kotlinx.serialization/SealedClassSerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization/SealedClassSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+
+    final fun findPolymorphicSerializerOrNull(kotlinx.serialization.encoding/CompositeDecoder, kotlin/String?): kotlinx.serialization/DeserializationStrategy<#A>? // kotlinx.serialization/SealedClassSerializer.findPolymorphicSerializerOrNull|findPolymorphicSerializerOrNull(kotlinx.serialization.encoding.CompositeDecoder;kotlin.String?){}[0]
+    final fun findPolymorphicSerializerOrNull(kotlinx.serialization.encoding/Encoder, #A): kotlinx.serialization/SerializationStrategy<#A>? // kotlinx.serialization/SealedClassSerializer.findPolymorphicSerializerOrNull|findPolymorphicSerializerOrNull(kotlinx.serialization.encoding.Encoder;1:0){}[0]
+}
+
+final class <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?> kotlinx.serialization.internal/TripleSerializer : kotlinx.serialization/KSerializer<kotlin/Triple<#A, #B, #C>> { // kotlinx.serialization.internal/TripleSerializer|null[0]
+    constructor <init>(kotlinx.serialization/KSerializer<#A>, kotlinx.serialization/KSerializer<#B>, kotlinx.serialization/KSerializer<#C>) // kotlinx.serialization.internal/TripleSerializer.<init>|<init>(kotlinx.serialization.KSerializer<1:0>;kotlinx.serialization.KSerializer<1:1>;kotlinx.serialization.KSerializer<1:2>){}[0]
+
+    final val descriptor // kotlinx.serialization.internal/TripleSerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.internal/TripleSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+
+    final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlin/Triple<#A, #B, #C> // kotlinx.serialization.internal/TripleSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
+    final fun serialize(kotlinx.serialization.encoding/Encoder, kotlin/Triple<#A, #B, #C>) // kotlinx.serialization.internal/TripleSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlin.Triple<1:0,1:1,1:2>){}[0]
+}
+
+final class <#A: kotlin/Any?, #B: kotlin/Any?> kotlinx.serialization.internal/HashMapSerializer : kotlinx.serialization.internal/MapLikeSerializer<#A, #B, kotlin.collections/Map<#A, #B>, kotlin.collections/HashMap<#A, #B>> { // kotlinx.serialization.internal/HashMapSerializer|null[0]
+    constructor <init>(kotlinx.serialization/KSerializer<#A>, kotlinx.serialization/KSerializer<#B>) // kotlinx.serialization.internal/HashMapSerializer.<init>|<init>(kotlinx.serialization.KSerializer<1:0>;kotlinx.serialization.KSerializer<1:1>){}[0]
+
+    final val descriptor // kotlinx.serialization.internal/HashMapSerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.internal/HashMapSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+}
+
+final class <#A: kotlin/Any?, #B: kotlin/Any?> kotlinx.serialization.internal/MapEntrySerializer : kotlinx.serialization.internal/KeyValueSerializer<#A, #B, kotlin.collections/Map.Entry<#A, #B>> { // kotlinx.serialization.internal/MapEntrySerializer|null[0]
+    constructor <init>(kotlinx.serialization/KSerializer<#A>, kotlinx.serialization/KSerializer<#B>) // kotlinx.serialization.internal/MapEntrySerializer.<init>|<init>(kotlinx.serialization.KSerializer<1:0>;kotlinx.serialization.KSerializer<1:1>){}[0]
+
+    final val descriptor // kotlinx.serialization.internal/MapEntrySerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.internal/MapEntrySerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+}
+
+final class <#A: kotlin/Any?, #B: kotlin/Any?> kotlinx.serialization.internal/PairSerializer : kotlinx.serialization.internal/KeyValueSerializer<#A, #B, kotlin/Pair<#A, #B>> { // kotlinx.serialization.internal/PairSerializer|null[0]
+    constructor <init>(kotlinx.serialization/KSerializer<#A>, kotlinx.serialization/KSerializer<#B>) // kotlinx.serialization.internal/PairSerializer.<init>|<init>(kotlinx.serialization.KSerializer<1:0>;kotlinx.serialization.KSerializer<1:1>){}[0]
+
+    final val descriptor // kotlinx.serialization.internal/PairSerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.internal/PairSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+}
+
+final class <#A: kotlin/Any?> kotlinx.serialization.internal/ArrayListSerializer : kotlinx.serialization.internal/CollectionSerializer<#A, kotlin.collections/List<#A>, kotlin.collections/ArrayList<#A>> { // kotlinx.serialization.internal/ArrayListSerializer|null[0]
+    constructor <init>(kotlinx.serialization/KSerializer<#A>) // kotlinx.serialization.internal/ArrayListSerializer.<init>|<init>(kotlinx.serialization.KSerializer<1:0>){}[0]
+
+    final val descriptor // kotlinx.serialization.internal/ArrayListSerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.internal/ArrayListSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+}
+
+final class <#A: kotlin/Any?> kotlinx.serialization.internal/HashSetSerializer : kotlinx.serialization.internal/CollectionSerializer<#A, kotlin.collections/Set<#A>, kotlin.collections/HashSet<#A>> { // kotlinx.serialization.internal/HashSetSerializer|null[0]
+    constructor <init>(kotlinx.serialization/KSerializer<#A>) // kotlinx.serialization.internal/HashSetSerializer.<init>|<init>(kotlinx.serialization.KSerializer<1:0>){}[0]
+
+    final val descriptor // kotlinx.serialization.internal/HashSetSerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.internal/HashSetSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+}
+
+final class <#A: kotlin/Enum<#A>> kotlinx.serialization.internal/EnumSerializer : kotlinx.serialization/KSerializer<#A> { // kotlinx.serialization.internal/EnumSerializer|null[0]
+    constructor <init>(kotlin/String, kotlin/Array<#A>) // kotlinx.serialization.internal/EnumSerializer.<init>|<init>(kotlin.String;kotlin.Array<1:0>){}[0]
+
+    final val descriptor // kotlinx.serialization.internal/EnumSerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.internal/EnumSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+
+    final fun deserialize(kotlinx.serialization.encoding/Decoder): #A // kotlinx.serialization.internal/EnumSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
+    final fun serialize(kotlinx.serialization.encoding/Encoder, #A) // kotlinx.serialization.internal/EnumSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;1:0){}[0]
+    final fun toString(): kotlin/String // kotlinx.serialization.internal/EnumSerializer.toString|toString(){}[0]
+}
+
+final class kotlinx.serialization.descriptors/ClassSerialDescriptorBuilder { // kotlinx.serialization.descriptors/ClassSerialDescriptorBuilder|null[0]
+    final val serialName // kotlinx.serialization.descriptors/ClassSerialDescriptorBuilder.serialName|{}serialName[0]
+        final fun <get-serialName>(): kotlin/String // kotlinx.serialization.descriptors/ClassSerialDescriptorBuilder.serialName.<get-serialName>|<get-serialName>(){}[0]
+
+    final var annotations // kotlinx.serialization.descriptors/ClassSerialDescriptorBuilder.annotations|{}annotations[0]
+        final fun <get-annotations>(): kotlin.collections/List<kotlin/Annotation> // kotlinx.serialization.descriptors/ClassSerialDescriptorBuilder.annotations.<get-annotations>|<get-annotations>(){}[0]
+        final fun <set-annotations>(kotlin.collections/List<kotlin/Annotation>) // kotlinx.serialization.descriptors/ClassSerialDescriptorBuilder.annotations.<set-annotations>|<set-annotations>(kotlin.collections.List<kotlin.Annotation>){}[0]
+    final var isNullable // kotlinx.serialization.descriptors/ClassSerialDescriptorBuilder.isNullable|{}isNullable[0]
+        final fun <get-isNullable>(): kotlin/Boolean // kotlinx.serialization.descriptors/ClassSerialDescriptorBuilder.isNullable.<get-isNullable>|<get-isNullable>(){}[0]
+        final fun <set-isNullable>(kotlin/Boolean) // kotlinx.serialization.descriptors/ClassSerialDescriptorBuilder.isNullable.<set-isNullable>|<set-isNullable>(kotlin.Boolean){}[0]
+
+    final fun element(kotlin/String, kotlinx.serialization.descriptors/SerialDescriptor, kotlin.collections/List<kotlin/Annotation> = ..., kotlin/Boolean = ...) // kotlinx.serialization.descriptors/ClassSerialDescriptorBuilder.element|element(kotlin.String;kotlinx.serialization.descriptors.SerialDescriptor;kotlin.collections.List<kotlin.Annotation>;kotlin.Boolean){}[0]
+}
+
+final class kotlinx.serialization.internal/BooleanArrayBuilder : kotlinx.serialization.internal/PrimitiveArrayBuilder<kotlin/BooleanArray> // kotlinx.serialization.internal/BooleanArrayBuilder|null[0]
+
+final class kotlinx.serialization.internal/ByteArrayBuilder : kotlinx.serialization.internal/PrimitiveArrayBuilder<kotlin/ByteArray> // kotlinx.serialization.internal/ByteArrayBuilder|null[0]
+
+final class kotlinx.serialization.internal/CharArrayBuilder : kotlinx.serialization.internal/PrimitiveArrayBuilder<kotlin/CharArray> // kotlinx.serialization.internal/CharArrayBuilder|null[0]
+
+final class kotlinx.serialization.internal/DoubleArrayBuilder : kotlinx.serialization.internal/PrimitiveArrayBuilder<kotlin/DoubleArray> // kotlinx.serialization.internal/DoubleArrayBuilder|null[0]
+
+final class kotlinx.serialization.internal/ElementMarker { // kotlinx.serialization.internal/ElementMarker|null[0]
+    constructor <init>(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Function2<kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/Boolean>) // kotlinx.serialization.internal/ElementMarker.<init>|<init>(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Function2<kotlinx.serialization.descriptors.SerialDescriptor,kotlin.Int,kotlin.Boolean>){}[0]
+
+    final fun mark(kotlin/Int) // kotlinx.serialization.internal/ElementMarker.mark|mark(kotlin.Int){}[0]
+    final fun nextUnmarkedIndex(): kotlin/Int // kotlinx.serialization.internal/ElementMarker.nextUnmarkedIndex|nextUnmarkedIndex(){}[0]
+}
+
+final class kotlinx.serialization.internal/EnumDescriptor : kotlinx.serialization.internal/PluginGeneratedSerialDescriptor { // kotlinx.serialization.internal/EnumDescriptor|null[0]
+    constructor <init>(kotlin/String, kotlin/Int) // kotlinx.serialization.internal/EnumDescriptor.<init>|<init>(kotlin.String;kotlin.Int){}[0]
+
+    final val kind // kotlinx.serialization.internal/EnumDescriptor.kind|{}kind[0]
+        final fun <get-kind>(): kotlinx.serialization.descriptors/SerialKind // kotlinx.serialization.internal/EnumDescriptor.kind.<get-kind>|<get-kind>(){}[0]
+
+    final fun equals(kotlin/Any?): kotlin/Boolean // kotlinx.serialization.internal/EnumDescriptor.equals|equals(kotlin.Any?){}[0]
+    final fun getElementDescriptor(kotlin/Int): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.internal/EnumDescriptor.getElementDescriptor|getElementDescriptor(kotlin.Int){}[0]
+    final fun hashCode(): kotlin/Int // kotlinx.serialization.internal/EnumDescriptor.hashCode|hashCode(){}[0]
+    final fun toString(): kotlin/String // kotlinx.serialization.internal/EnumDescriptor.toString|toString(){}[0]
+}
+
+final class kotlinx.serialization.internal/FloatArrayBuilder : kotlinx.serialization.internal/PrimitiveArrayBuilder<kotlin/FloatArray> // kotlinx.serialization.internal/FloatArrayBuilder|null[0]
+
+final class kotlinx.serialization.internal/InlineClassDescriptor : kotlinx.serialization.internal/PluginGeneratedSerialDescriptor { // kotlinx.serialization.internal/InlineClassDescriptor|null[0]
+    constructor <init>(kotlin/String, kotlinx.serialization.internal/GeneratedSerializer<*>) // kotlinx.serialization.internal/InlineClassDescriptor.<init>|<init>(kotlin.String;kotlinx.serialization.internal.GeneratedSerializer<*>){}[0]
+
+    final val isInline // kotlinx.serialization.internal/InlineClassDescriptor.isInline|{}isInline[0]
+        final fun <get-isInline>(): kotlin/Boolean // kotlinx.serialization.internal/InlineClassDescriptor.isInline.<get-isInline>|<get-isInline>(){}[0]
+
+    final fun equals(kotlin/Any?): kotlin/Boolean // kotlinx.serialization.internal/InlineClassDescriptor.equals|equals(kotlin.Any?){}[0]
+    final fun hashCode(): kotlin/Int // kotlinx.serialization.internal/InlineClassDescriptor.hashCode|hashCode(){}[0]
+}
+
+final class kotlinx.serialization.internal/IntArrayBuilder : kotlinx.serialization.internal/PrimitiveArrayBuilder<kotlin/IntArray> // kotlinx.serialization.internal/IntArrayBuilder|null[0]
+
+final class kotlinx.serialization.internal/LongArrayBuilder : kotlinx.serialization.internal/PrimitiveArrayBuilder<kotlin/LongArray> // kotlinx.serialization.internal/LongArrayBuilder|null[0]
+
+final class kotlinx.serialization.internal/SerializationConstructorMarker // kotlinx.serialization.internal/SerializationConstructorMarker|null[0]
+
+final class kotlinx.serialization.internal/ShortArrayBuilder : kotlinx.serialization.internal/PrimitiveArrayBuilder<kotlin/ShortArray> // kotlinx.serialization.internal/ShortArrayBuilder|null[0]
+
+final class kotlinx.serialization.internal/UByteArrayBuilder : kotlinx.serialization.internal/PrimitiveArrayBuilder<kotlin/UByteArray> // kotlinx.serialization.internal/UByteArrayBuilder|null[0]
+
+final class kotlinx.serialization.internal/UIntArrayBuilder : kotlinx.serialization.internal/PrimitiveArrayBuilder<kotlin/UIntArray> // kotlinx.serialization.internal/UIntArrayBuilder|null[0]
+
+final class kotlinx.serialization.internal/ULongArrayBuilder : kotlinx.serialization.internal/PrimitiveArrayBuilder<kotlin/ULongArray> // kotlinx.serialization.internal/ULongArrayBuilder|null[0]
+
+final class kotlinx.serialization.internal/UShortArrayBuilder : kotlinx.serialization.internal/PrimitiveArrayBuilder<kotlin/UShortArray> // kotlinx.serialization.internal/UShortArrayBuilder|null[0]
+
+final class kotlinx.serialization.modules/SerializersModuleBuilder : kotlinx.serialization.modules/SerializersModuleCollector { // kotlinx.serialization.modules/SerializersModuleBuilder|null[0]
+    constructor <init>() // kotlinx.serialization.modules/SerializersModuleBuilder.<init>|<init>(){}[0]
+
+    final fun <#A1: kotlin/Any, #B1: #A1> polymorphic(kotlin.reflect/KClass<#A1>, kotlin.reflect/KClass<#B1>, kotlinx.serialization/KSerializer<#B1>) // kotlinx.serialization.modules/SerializersModuleBuilder.polymorphic|polymorphic(kotlin.reflect.KClass<0:0>;kotlin.reflect.KClass<0:1>;kotlinx.serialization.KSerializer<0:1>){0§<kotlin.Any>;1§<0:0>}[0]
+    final fun <#A1: kotlin/Any> contextual(kotlin.reflect/KClass<#A1>, kotlin/Function1<kotlin.collections/List<kotlinx.serialization/KSerializer<*>>, kotlinx.serialization/KSerializer<*>>) // kotlinx.serialization.modules/SerializersModuleBuilder.contextual|contextual(kotlin.reflect.KClass<0:0>;kotlin.Function1<kotlin.collections.List<kotlinx.serialization.KSerializer<*>>,kotlinx.serialization.KSerializer<*>>){0§<kotlin.Any>}[0]
+    final fun <#A1: kotlin/Any> contextual(kotlin.reflect/KClass<#A1>, kotlinx.serialization/KSerializer<#A1>) // kotlinx.serialization.modules/SerializersModuleBuilder.contextual|contextual(kotlin.reflect.KClass<0:0>;kotlinx.serialization.KSerializer<0:0>){0§<kotlin.Any>}[0]
+    final fun <#A1: kotlin/Any> polymorphicDefaultDeserializer(kotlin.reflect/KClass<#A1>, kotlin/Function1<kotlin/String?, kotlinx.serialization/DeserializationStrategy<#A1>?>) // kotlinx.serialization.modules/SerializersModuleBuilder.polymorphicDefaultDeserializer|polymorphicDefaultDeserializer(kotlin.reflect.KClass<0:0>;kotlin.Function1<kotlin.String?,kotlinx.serialization.DeserializationStrategy<0:0>?>){0§<kotlin.Any>}[0]
+    final fun <#A1: kotlin/Any> polymorphicDefaultSerializer(kotlin.reflect/KClass<#A1>, kotlin/Function1<#A1, kotlinx.serialization/SerializationStrategy<#A1>?>) // kotlinx.serialization.modules/SerializersModuleBuilder.polymorphicDefaultSerializer|polymorphicDefaultSerializer(kotlin.reflect.KClass<0:0>;kotlin.Function1<0:0,kotlinx.serialization.SerializationStrategy<0:0>?>){0§<kotlin.Any>}[0]
+    final fun build(): kotlinx.serialization.modules/SerializersModule // kotlinx.serialization.modules/SerializersModuleBuilder.build|build(){}[0]
+    final fun include(kotlinx.serialization.modules/SerializersModule) // kotlinx.serialization.modules/SerializersModuleBuilder.include|include(kotlinx.serialization.modules.SerializersModule){}[0]
+}
+
+final class kotlinx.serialization/MissingFieldException : kotlinx.serialization/SerializationException { // kotlinx.serialization/MissingFieldException|null[0]
+    constructor <init>(kotlin.collections/List<kotlin/String>, kotlin/String) // kotlinx.serialization/MissingFieldException.<init>|<init>(kotlin.collections.List<kotlin.String>;kotlin.String){}[0]
+    constructor <init>(kotlin.collections/List<kotlin/String>, kotlin/String?, kotlin/Throwable?) // kotlinx.serialization/MissingFieldException.<init>|<init>(kotlin.collections.List<kotlin.String>;kotlin.String?;kotlin.Throwable?){}[0]
+    constructor <init>(kotlin/String) // kotlinx.serialization/MissingFieldException.<init>|<init>(kotlin.String){}[0]
+    constructor <init>(kotlin/String, kotlin/String) // kotlinx.serialization/MissingFieldException.<init>|<init>(kotlin.String;kotlin.String){}[0]
+
+    final val missingFields // kotlinx.serialization/MissingFieldException.missingFields|{}missingFields[0]
+        final fun <get-missingFields>(): kotlin.collections/List<kotlin/String> // kotlinx.serialization/MissingFieldException.missingFields.<get-missingFields>|<get-missingFields>(){}[0]
+}
+
+final class kotlinx.serialization/UnknownFieldException : kotlinx.serialization/SerializationException { // kotlinx.serialization/UnknownFieldException|null[0]
+    constructor <init>(kotlin/Int) // kotlinx.serialization/UnknownFieldException.<init>|<init>(kotlin.Int){}[0]
+}
+
+open class kotlinx.serialization.internal/PluginGeneratedSerialDescriptor : kotlinx.serialization.descriptors/SerialDescriptor, kotlinx.serialization.internal/CachedNames { // kotlinx.serialization.internal/PluginGeneratedSerialDescriptor|null[0]
+    constructor <init>(kotlin/String, kotlinx.serialization.internal/GeneratedSerializer<*>? = ..., kotlin/Int) // kotlinx.serialization.internal/PluginGeneratedSerialDescriptor.<init>|<init>(kotlin.String;kotlinx.serialization.internal.GeneratedSerializer<*>?;kotlin.Int){}[0]
+
+    final val elementsCount // kotlinx.serialization.internal/PluginGeneratedSerialDescriptor.elementsCount|{}elementsCount[0]
+        final fun <get-elementsCount>(): kotlin/Int // kotlinx.serialization.internal/PluginGeneratedSerialDescriptor.elementsCount.<get-elementsCount>|<get-elementsCount>(){}[0]
+    open val annotations // kotlinx.serialization.internal/PluginGeneratedSerialDescriptor.annotations|{}annotations[0]
+        open fun <get-annotations>(): kotlin.collections/List<kotlin/Annotation> // kotlinx.serialization.internal/PluginGeneratedSerialDescriptor.annotations.<get-annotations>|<get-annotations>(){}[0]
+    open val kind // kotlinx.serialization.internal/PluginGeneratedSerialDescriptor.kind|{}kind[0]
+        open fun <get-kind>(): kotlinx.serialization.descriptors/SerialKind // kotlinx.serialization.internal/PluginGeneratedSerialDescriptor.kind.<get-kind>|<get-kind>(){}[0]
+    open val serialName // kotlinx.serialization.internal/PluginGeneratedSerialDescriptor.serialName|{}serialName[0]
+        open fun <get-serialName>(): kotlin/String // kotlinx.serialization.internal/PluginGeneratedSerialDescriptor.serialName.<get-serialName>|<get-serialName>(){}[0]
+    open val serialNames // kotlinx.serialization.internal/PluginGeneratedSerialDescriptor.serialNames|{}serialNames[0]
+        open fun <get-serialNames>(): kotlin.collections/Set<kotlin/String> // kotlinx.serialization.internal/PluginGeneratedSerialDescriptor.serialNames.<get-serialNames>|<get-serialNames>(){}[0]
+
+    final fun addElement(kotlin/String, kotlin/Boolean = ...) // kotlinx.serialization.internal/PluginGeneratedSerialDescriptor.addElement|addElement(kotlin.String;kotlin.Boolean){}[0]
+    final fun pushAnnotation(kotlin/Annotation) // kotlinx.serialization.internal/PluginGeneratedSerialDescriptor.pushAnnotation|pushAnnotation(kotlin.Annotation){}[0]
+    final fun pushClassAnnotation(kotlin/Annotation) // kotlinx.serialization.internal/PluginGeneratedSerialDescriptor.pushClassAnnotation|pushClassAnnotation(kotlin.Annotation){}[0]
+    open fun equals(kotlin/Any?): kotlin/Boolean // kotlinx.serialization.internal/PluginGeneratedSerialDescriptor.equals|equals(kotlin.Any?){}[0]
+    open fun getElementAnnotations(kotlin/Int): kotlin.collections/List<kotlin/Annotation> // kotlinx.serialization.internal/PluginGeneratedSerialDescriptor.getElementAnnotations|getElementAnnotations(kotlin.Int){}[0]
+    open fun getElementDescriptor(kotlin/Int): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.internal/PluginGeneratedSerialDescriptor.getElementDescriptor|getElementDescriptor(kotlin.Int){}[0]
+    open fun getElementIndex(kotlin/String): kotlin/Int // kotlinx.serialization.internal/PluginGeneratedSerialDescriptor.getElementIndex|getElementIndex(kotlin.String){}[0]
+    open fun getElementName(kotlin/Int): kotlin/String // kotlinx.serialization.internal/PluginGeneratedSerialDescriptor.getElementName|getElementName(kotlin.Int){}[0]
+    open fun hashCode(): kotlin/Int // kotlinx.serialization.internal/PluginGeneratedSerialDescriptor.hashCode|hashCode(){}[0]
+    open fun isElementOptional(kotlin/Int): kotlin/Boolean // kotlinx.serialization.internal/PluginGeneratedSerialDescriptor.isElementOptional|isElementOptional(kotlin.Int){}[0]
+    open fun toString(): kotlin/String // kotlinx.serialization.internal/PluginGeneratedSerialDescriptor.toString|toString(){}[0]
+}
+
+open class kotlinx.serialization/SerializationException : kotlin/IllegalArgumentException { // kotlinx.serialization/SerializationException|null[0]
+    constructor <init>() // kotlinx.serialization/SerializationException.<init>|<init>(){}[0]
+    constructor <init>(kotlin/String?) // kotlinx.serialization/SerializationException.<init>|<init>(kotlin.String?){}[0]
+    constructor <init>(kotlin/String?, kotlin/Throwable?) // kotlinx.serialization/SerializationException.<init>|<init>(kotlin.String?;kotlin.Throwable?){}[0]
+    constructor <init>(kotlin/Throwable?) // kotlinx.serialization/SerializationException.<init>|<init>(kotlin.Throwable?){}[0]
+}
+
+sealed class <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?, #D: kotlin.collections/MutableMap<#A, #B>> kotlinx.serialization.internal/MapLikeSerializer : kotlinx.serialization.internal/AbstractCollectionSerializer<kotlin.collections/Map.Entry<#A, #B>, #C, #D> { // kotlinx.serialization.internal/MapLikeSerializer|null[0]
+    abstract val descriptor // kotlinx.serialization.internal/MapLikeSerializer.descriptor|{}descriptor[0]
+        abstract fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.internal/MapLikeSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+    final val keySerializer // kotlinx.serialization.internal/MapLikeSerializer.keySerializer|{}keySerializer[0]
+        final fun <get-keySerializer>(): kotlinx.serialization/KSerializer<#A> // kotlinx.serialization.internal/MapLikeSerializer.keySerializer.<get-keySerializer>|<get-keySerializer>(){}[0]
+    final val valueSerializer // kotlinx.serialization.internal/MapLikeSerializer.valueSerializer|{}valueSerializer[0]
+        final fun <get-valueSerializer>(): kotlinx.serialization/KSerializer<#B> // kotlinx.serialization.internal/MapLikeSerializer.valueSerializer.<get-valueSerializer>|<get-valueSerializer>(){}[0]
+
+    abstract fun (#D).insertKeyValuePair(kotlin/Int, #A, #B) // kotlinx.serialization.internal/MapLikeSerializer.insertKeyValuePair|insertKeyValuePair@1:3(kotlin.Int;1:0;1:1){}[0]
+    final fun readAll(kotlinx.serialization.encoding/CompositeDecoder, #D, kotlin/Int, kotlin/Int) // kotlinx.serialization.internal/MapLikeSerializer.readAll|readAll(kotlinx.serialization.encoding.CompositeDecoder;1:3;kotlin.Int;kotlin.Int){}[0]
+    final fun readElement(kotlinx.serialization.encoding/CompositeDecoder, kotlin/Int, #D, kotlin/Boolean) // kotlinx.serialization.internal/MapLikeSerializer.readElement|readElement(kotlinx.serialization.encoding.CompositeDecoder;kotlin.Int;1:3;kotlin.Boolean){}[0]
+    open fun serialize(kotlinx.serialization.encoding/Encoder, #C) // kotlinx.serialization.internal/MapLikeSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;1:2){}[0]
+}
+
+sealed class <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?> kotlinx.serialization.internal/AbstractCollectionSerializer : kotlinx.serialization/KSerializer<#B> { // kotlinx.serialization.internal/AbstractCollectionSerializer|null[0]
+    abstract fun (#B).collectionIterator(): kotlin.collections/Iterator<#A> // kotlinx.serialization.internal/AbstractCollectionSerializer.collectionIterator|collectionIterator@1:1(){}[0]
+    abstract fun (#B).collectionSize(): kotlin/Int // kotlinx.serialization.internal/AbstractCollectionSerializer.collectionSize|collectionSize@1:1(){}[0]
+    abstract fun (#B).toBuilder(): #C // kotlinx.serialization.internal/AbstractCollectionSerializer.toBuilder|toBuilder@1:1(){}[0]
+    abstract fun (#C).builderSize(): kotlin/Int // kotlinx.serialization.internal/AbstractCollectionSerializer.builderSize|builderSize@1:2(){}[0]
+    abstract fun (#C).checkCapacity(kotlin/Int) // kotlinx.serialization.internal/AbstractCollectionSerializer.checkCapacity|checkCapacity@1:2(kotlin.Int){}[0]
+    abstract fun (#C).toResult(): #B // kotlinx.serialization.internal/AbstractCollectionSerializer.toResult|toResult@1:2(){}[0]
+    abstract fun builder(): #C // kotlinx.serialization.internal/AbstractCollectionSerializer.builder|builder(){}[0]
+    abstract fun readAll(kotlinx.serialization.encoding/CompositeDecoder, #C, kotlin/Int, kotlin/Int) // kotlinx.serialization.internal/AbstractCollectionSerializer.readAll|readAll(kotlinx.serialization.encoding.CompositeDecoder;1:2;kotlin.Int;kotlin.Int){}[0]
+    abstract fun readElement(kotlinx.serialization.encoding/CompositeDecoder, kotlin/Int, #C, kotlin/Boolean = ...) // kotlinx.serialization.internal/AbstractCollectionSerializer.readElement|readElement(kotlinx.serialization.encoding.CompositeDecoder;kotlin.Int;1:2;kotlin.Boolean){}[0]
+    abstract fun serialize(kotlinx.serialization.encoding/Encoder, #B) // kotlinx.serialization.internal/AbstractCollectionSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;1:1){}[0]
+    final fun merge(kotlinx.serialization.encoding/Decoder, #B?): #B // kotlinx.serialization.internal/AbstractCollectionSerializer.merge|merge(kotlinx.serialization.encoding.Decoder;1:1?){}[0]
+    open fun deserialize(kotlinx.serialization.encoding/Decoder): #B // kotlinx.serialization.internal/AbstractCollectionSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
+}
+
+sealed class <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?> kotlinx.serialization.internal/CollectionLikeSerializer : kotlinx.serialization.internal/AbstractCollectionSerializer<#A, #B, #C> { // kotlinx.serialization.internal/CollectionLikeSerializer|null[0]
+    abstract val descriptor // kotlinx.serialization.internal/CollectionLikeSerializer.descriptor|{}descriptor[0]
+        abstract fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.internal/CollectionLikeSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+
+    abstract fun (#C).insert(kotlin/Int, #A) // kotlinx.serialization.internal/CollectionLikeSerializer.insert|insert@1:2(kotlin.Int;1:0){}[0]
+    final fun readAll(kotlinx.serialization.encoding/CompositeDecoder, #C, kotlin/Int, kotlin/Int) // kotlinx.serialization.internal/CollectionLikeSerializer.readAll|readAll(kotlinx.serialization.encoding.CompositeDecoder;1:2;kotlin.Int;kotlin.Int){}[0]
+    open fun readElement(kotlinx.serialization.encoding/CompositeDecoder, kotlin/Int, #C, kotlin/Boolean) // kotlinx.serialization.internal/CollectionLikeSerializer.readElement|readElement(kotlinx.serialization.encoding.CompositeDecoder;kotlin.Int;1:2;kotlin.Boolean){}[0]
+    open fun serialize(kotlinx.serialization.encoding/Encoder, #B) // kotlinx.serialization.internal/CollectionLikeSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;1:1){}[0]
+}
+
+sealed class <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?> kotlinx.serialization.internal/KeyValueSerializer : kotlinx.serialization/KSerializer<#C> { // kotlinx.serialization.internal/KeyValueSerializer|null[0]
+    abstract val key // kotlinx.serialization.internal/KeyValueSerializer.key|@1:2{}key[0]
+        abstract fun (#C).<get-key>(): #A // kotlinx.serialization.internal/KeyValueSerializer.key.<get-key>|<get-key>@1:2(){}[0]
+    abstract val value // kotlinx.serialization.internal/KeyValueSerializer.value|@1:2{}value[0]
+        abstract fun (#C).<get-value>(): #B // kotlinx.serialization.internal/KeyValueSerializer.value.<get-value>|<get-value>@1:2(){}[0]
+    final val keySerializer // kotlinx.serialization.internal/KeyValueSerializer.keySerializer|{}keySerializer[0]
+        final fun <get-keySerializer>(): kotlinx.serialization/KSerializer<#A> // kotlinx.serialization.internal/KeyValueSerializer.keySerializer.<get-keySerializer>|<get-keySerializer>(){}[0]
+    final val valueSerializer // kotlinx.serialization.internal/KeyValueSerializer.valueSerializer|{}valueSerializer[0]
+        final fun <get-valueSerializer>(): kotlinx.serialization/KSerializer<#B> // kotlinx.serialization.internal/KeyValueSerializer.valueSerializer.<get-valueSerializer>|<get-valueSerializer>(){}[0]
+
+    abstract fun toResult(#A, #B): #C // kotlinx.serialization.internal/KeyValueSerializer.toResult|toResult(1:0;1:1){}[0]
+    open fun deserialize(kotlinx.serialization.encoding/Decoder): #C // kotlinx.serialization.internal/KeyValueSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
+    open fun serialize(kotlinx.serialization.encoding/Encoder, #C) // kotlinx.serialization.internal/KeyValueSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;1:2){}[0]
+}
+
+sealed class kotlinx.serialization.descriptors/PolymorphicKind : kotlinx.serialization.descriptors/SerialKind { // kotlinx.serialization.descriptors/PolymorphicKind|null[0]
+    final object OPEN : kotlinx.serialization.descriptors/PolymorphicKind // kotlinx.serialization.descriptors/PolymorphicKind.OPEN|null[0]
+
+    final object SEALED : kotlinx.serialization.descriptors/PolymorphicKind // kotlinx.serialization.descriptors/PolymorphicKind.SEALED|null[0]
+}
+
+sealed class kotlinx.serialization.descriptors/PrimitiveKind : kotlinx.serialization.descriptors/SerialKind { // kotlinx.serialization.descriptors/PrimitiveKind|null[0]
+    final object BOOLEAN : kotlinx.serialization.descriptors/PrimitiveKind // kotlinx.serialization.descriptors/PrimitiveKind.BOOLEAN|null[0]
+
+    final object BYTE : kotlinx.serialization.descriptors/PrimitiveKind // kotlinx.serialization.descriptors/PrimitiveKind.BYTE|null[0]
+
+    final object CHAR : kotlinx.serialization.descriptors/PrimitiveKind // kotlinx.serialization.descriptors/PrimitiveKind.CHAR|null[0]
+
+    final object DOUBLE : kotlinx.serialization.descriptors/PrimitiveKind // kotlinx.serialization.descriptors/PrimitiveKind.DOUBLE|null[0]
+
+    final object FLOAT : kotlinx.serialization.descriptors/PrimitiveKind // kotlinx.serialization.descriptors/PrimitiveKind.FLOAT|null[0]
+
+    final object INT : kotlinx.serialization.descriptors/PrimitiveKind // kotlinx.serialization.descriptors/PrimitiveKind.INT|null[0]
+
+    final object LONG : kotlinx.serialization.descriptors/PrimitiveKind // kotlinx.serialization.descriptors/PrimitiveKind.LONG|null[0]
+
+    final object SHORT : kotlinx.serialization.descriptors/PrimitiveKind // kotlinx.serialization.descriptors/PrimitiveKind.SHORT|null[0]
+
+    final object STRING : kotlinx.serialization.descriptors/PrimitiveKind // kotlinx.serialization.descriptors/PrimitiveKind.STRING|null[0]
+}
+
+sealed class kotlinx.serialization.descriptors/SerialKind { // kotlinx.serialization.descriptors/SerialKind|null[0]
+    open fun hashCode(): kotlin/Int // kotlinx.serialization.descriptors/SerialKind.hashCode|hashCode(){}[0]
+    open fun toString(): kotlin/String // kotlinx.serialization.descriptors/SerialKind.toString|toString(){}[0]
+
+    final object CONTEXTUAL : kotlinx.serialization.descriptors/SerialKind // kotlinx.serialization.descriptors/SerialKind.CONTEXTUAL|null[0]
+
+    final object ENUM : kotlinx.serialization.descriptors/SerialKind // kotlinx.serialization.descriptors/SerialKind.ENUM|null[0]
+}
+
+sealed class kotlinx.serialization.descriptors/StructureKind : kotlinx.serialization.descriptors/SerialKind { // kotlinx.serialization.descriptors/StructureKind|null[0]
+    final object CLASS : kotlinx.serialization.descriptors/StructureKind // kotlinx.serialization.descriptors/StructureKind.CLASS|null[0]
+
+    final object LIST : kotlinx.serialization.descriptors/StructureKind // kotlinx.serialization.descriptors/StructureKind.LIST|null[0]
+
+    final object MAP : kotlinx.serialization.descriptors/StructureKind // kotlinx.serialization.descriptors/StructureKind.MAP|null[0]
+
+    final object OBJECT : kotlinx.serialization.descriptors/StructureKind // kotlinx.serialization.descriptors/StructureKind.OBJECT|null[0]
+}
+
+sealed class kotlinx.serialization.modules/SerializersModule { // kotlinx.serialization.modules/SerializersModule|null[0]
+    abstract fun <#A1: kotlin/Any> getContextual(kotlin.reflect/KClass<#A1>, kotlin.collections/List<kotlinx.serialization/KSerializer<*>> = ...): kotlinx.serialization/KSerializer<#A1>? // kotlinx.serialization.modules/SerializersModule.getContextual|getContextual(kotlin.reflect.KClass<0:0>;kotlin.collections.List<kotlinx.serialization.KSerializer<*>>){0§<kotlin.Any>}[0]
+    abstract fun <#A1: kotlin/Any> getPolymorphic(kotlin.reflect/KClass<in #A1>, #A1): kotlinx.serialization/SerializationStrategy<#A1>? // kotlinx.serialization.modules/SerializersModule.getPolymorphic|getPolymorphic(kotlin.reflect.KClass<in|0:0>;0:0){0§<kotlin.Any>}[0]
+    abstract fun <#A1: kotlin/Any> getPolymorphic(kotlin.reflect/KClass<in #A1>, kotlin/String?): kotlinx.serialization/DeserializationStrategy<#A1>? // kotlinx.serialization.modules/SerializersModule.getPolymorphic|getPolymorphic(kotlin.reflect.KClass<in|0:0>;kotlin.String?){0§<kotlin.Any>}[0]
+    abstract fun dumpTo(kotlinx.serialization.modules/SerializersModuleCollector) // kotlinx.serialization.modules/SerializersModule.dumpTo|dumpTo(kotlinx.serialization.modules.SerializersModuleCollector){}[0]
+    final fun <#A1: kotlin/Any> getContextual(kotlin.reflect/KClass<#A1>): kotlinx.serialization/KSerializer<#A1>? // kotlinx.serialization.modules/SerializersModule.getContextual|getContextual(kotlin.reflect.KClass<0:0>){0§<kotlin.Any>}[0]
+}
+
+final object kotlinx.serialization.builtins/LongAsStringSerializer : kotlinx.serialization/KSerializer<kotlin/Long> { // kotlinx.serialization.builtins/LongAsStringSerializer|null[0]
+    final val descriptor // kotlinx.serialization.builtins/LongAsStringSerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.builtins/LongAsStringSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+
+    final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlin/Long // kotlinx.serialization.builtins/LongAsStringSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
+    final fun serialize(kotlinx.serialization.encoding/Encoder, kotlin/Long) // kotlinx.serialization.builtins/LongAsStringSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlin.Long){}[0]
+}
+
+final object kotlinx.serialization.internal/BooleanArraySerializer : kotlinx.serialization.internal/PrimitiveArraySerializer<kotlin/Boolean, kotlin/BooleanArray, kotlinx.serialization.internal/BooleanArrayBuilder>, kotlinx.serialization/KSerializer<kotlin/BooleanArray> // kotlinx.serialization.internal/BooleanArraySerializer|null[0]
+
+final object kotlinx.serialization.internal/BooleanSerializer : kotlinx.serialization/KSerializer<kotlin/Boolean> { // kotlinx.serialization.internal/BooleanSerializer|null[0]
+    final val descriptor // kotlinx.serialization.internal/BooleanSerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.internal/BooleanSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+
+    final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlin/Boolean // kotlinx.serialization.internal/BooleanSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
+    final fun serialize(kotlinx.serialization.encoding/Encoder, kotlin/Boolean) // kotlinx.serialization.internal/BooleanSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlin.Boolean){}[0]
+}
+
+final object kotlinx.serialization.internal/ByteArraySerializer : kotlinx.serialization.internal/PrimitiveArraySerializer<kotlin/Byte, kotlin/ByteArray, kotlinx.serialization.internal/ByteArrayBuilder>, kotlinx.serialization/KSerializer<kotlin/ByteArray> // kotlinx.serialization.internal/ByteArraySerializer|null[0]
+
+final object kotlinx.serialization.internal/ByteSerializer : kotlinx.serialization/KSerializer<kotlin/Byte> { // kotlinx.serialization.internal/ByteSerializer|null[0]
+    final val descriptor // kotlinx.serialization.internal/ByteSerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.internal/ByteSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+
+    final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlin/Byte // kotlinx.serialization.internal/ByteSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
+    final fun serialize(kotlinx.serialization.encoding/Encoder, kotlin/Byte) // kotlinx.serialization.internal/ByteSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlin.Byte){}[0]
+}
+
+final object kotlinx.serialization.internal/CharArraySerializer : kotlinx.serialization.internal/PrimitiveArraySerializer<kotlin/Char, kotlin/CharArray, kotlinx.serialization.internal/CharArrayBuilder>, kotlinx.serialization/KSerializer<kotlin/CharArray> // kotlinx.serialization.internal/CharArraySerializer|null[0]
+
+final object kotlinx.serialization.internal/CharSerializer : kotlinx.serialization/KSerializer<kotlin/Char> { // kotlinx.serialization.internal/CharSerializer|null[0]
+    final val descriptor // kotlinx.serialization.internal/CharSerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.internal/CharSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+
+    final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlin/Char // kotlinx.serialization.internal/CharSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
+    final fun serialize(kotlinx.serialization.encoding/Encoder, kotlin/Char) // kotlinx.serialization.internal/CharSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlin.Char){}[0]
+}
+
+final object kotlinx.serialization.internal/DoubleArraySerializer : kotlinx.serialization.internal/PrimitiveArraySerializer<kotlin/Double, kotlin/DoubleArray, kotlinx.serialization.internal/DoubleArrayBuilder>, kotlinx.serialization/KSerializer<kotlin/DoubleArray> // kotlinx.serialization.internal/DoubleArraySerializer|null[0]
+
+final object kotlinx.serialization.internal/DoubleSerializer : kotlinx.serialization/KSerializer<kotlin/Double> { // kotlinx.serialization.internal/DoubleSerializer|null[0]
+    final val descriptor // kotlinx.serialization.internal/DoubleSerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.internal/DoubleSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+
+    final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlin/Double // kotlinx.serialization.internal/DoubleSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
+    final fun serialize(kotlinx.serialization.encoding/Encoder, kotlin/Double) // kotlinx.serialization.internal/DoubleSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlin.Double){}[0]
+}
+
+final object kotlinx.serialization.internal/DurationSerializer : kotlinx.serialization/KSerializer<kotlin.time/Duration> { // kotlinx.serialization.internal/DurationSerializer|null[0]
+    final val descriptor // kotlinx.serialization.internal/DurationSerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.internal/DurationSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+
+    final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlin.time/Duration // kotlinx.serialization.internal/DurationSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
+    final fun serialize(kotlinx.serialization.encoding/Encoder, kotlin.time/Duration) // kotlinx.serialization.internal/DurationSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlin.time.Duration){}[0]
+}
+
+final object kotlinx.serialization.internal/FloatArraySerializer : kotlinx.serialization.internal/PrimitiveArraySerializer<kotlin/Float, kotlin/FloatArray, kotlinx.serialization.internal/FloatArrayBuilder>, kotlinx.serialization/KSerializer<kotlin/FloatArray> // kotlinx.serialization.internal/FloatArraySerializer|null[0]
+
+final object kotlinx.serialization.internal/FloatSerializer : kotlinx.serialization/KSerializer<kotlin/Float> { // kotlinx.serialization.internal/FloatSerializer|null[0]
+    final val descriptor // kotlinx.serialization.internal/FloatSerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.internal/FloatSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+
+    final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlin/Float // kotlinx.serialization.internal/FloatSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
+    final fun serialize(kotlinx.serialization.encoding/Encoder, kotlin/Float) // kotlinx.serialization.internal/FloatSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlin.Float){}[0]
+}
+
+final object kotlinx.serialization.internal/IntArraySerializer : kotlinx.serialization.internal/PrimitiveArraySerializer<kotlin/Int, kotlin/IntArray, kotlinx.serialization.internal/IntArrayBuilder>, kotlinx.serialization/KSerializer<kotlin/IntArray> // kotlinx.serialization.internal/IntArraySerializer|null[0]
+
+final object kotlinx.serialization.internal/IntSerializer : kotlinx.serialization/KSerializer<kotlin/Int> { // kotlinx.serialization.internal/IntSerializer|null[0]
+    final val descriptor // kotlinx.serialization.internal/IntSerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.internal/IntSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+
+    final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlin/Int // kotlinx.serialization.internal/IntSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
+    final fun serialize(kotlinx.serialization.encoding/Encoder, kotlin/Int) // kotlinx.serialization.internal/IntSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlin.Int){}[0]
+}
+
+final object kotlinx.serialization.internal/LongArraySerializer : kotlinx.serialization.internal/PrimitiveArraySerializer<kotlin/Long, kotlin/LongArray, kotlinx.serialization.internal/LongArrayBuilder>, kotlinx.serialization/KSerializer<kotlin/LongArray> // kotlinx.serialization.internal/LongArraySerializer|null[0]
+
+final object kotlinx.serialization.internal/LongSerializer : kotlinx.serialization/KSerializer<kotlin/Long> { // kotlinx.serialization.internal/LongSerializer|null[0]
+    final val descriptor // kotlinx.serialization.internal/LongSerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.internal/LongSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+
+    final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlin/Long // kotlinx.serialization.internal/LongSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
+    final fun serialize(kotlinx.serialization.encoding/Encoder, kotlin/Long) // kotlinx.serialization.internal/LongSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlin.Long){}[0]
+}
+
+final object kotlinx.serialization.internal/NothingSerializer : kotlinx.serialization/KSerializer<kotlin/Nothing> { // kotlinx.serialization.internal/NothingSerializer|null[0]
+    final val descriptor // kotlinx.serialization.internal/NothingSerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.internal/NothingSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+
+    final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlin/Nothing // kotlinx.serialization.internal/NothingSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
+    final fun serialize(kotlinx.serialization.encoding/Encoder, kotlin/Nothing) // kotlinx.serialization.internal/NothingSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlin.Nothing){}[0]
+}
+
+final object kotlinx.serialization.internal/ShortArraySerializer : kotlinx.serialization.internal/PrimitiveArraySerializer<kotlin/Short, kotlin/ShortArray, kotlinx.serialization.internal/ShortArrayBuilder>, kotlinx.serialization/KSerializer<kotlin/ShortArray> // kotlinx.serialization.internal/ShortArraySerializer|null[0]
+
+final object kotlinx.serialization.internal/ShortSerializer : kotlinx.serialization/KSerializer<kotlin/Short> { // kotlinx.serialization.internal/ShortSerializer|null[0]
+    final val descriptor // kotlinx.serialization.internal/ShortSerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.internal/ShortSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+
+    final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlin/Short // kotlinx.serialization.internal/ShortSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
+    final fun serialize(kotlinx.serialization.encoding/Encoder, kotlin/Short) // kotlinx.serialization.internal/ShortSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlin.Short){}[0]
+}
+
+final object kotlinx.serialization.internal/StringSerializer : kotlinx.serialization/KSerializer<kotlin/String> { // kotlinx.serialization.internal/StringSerializer|null[0]
+    final val descriptor // kotlinx.serialization.internal/StringSerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.internal/StringSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+
+    final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlin/String // kotlinx.serialization.internal/StringSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
+    final fun serialize(kotlinx.serialization.encoding/Encoder, kotlin/String) // kotlinx.serialization.internal/StringSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlin.String){}[0]
+}
+
+final object kotlinx.serialization.internal/UByteArraySerializer : kotlinx.serialization.internal/PrimitiveArraySerializer<kotlin/UByte, kotlin/UByteArray, kotlinx.serialization.internal/UByteArrayBuilder>, kotlinx.serialization/KSerializer<kotlin/UByteArray> // kotlinx.serialization.internal/UByteArraySerializer|null[0]
+
+final object kotlinx.serialization.internal/UByteSerializer : kotlinx.serialization/KSerializer<kotlin/UByte> { // kotlinx.serialization.internal/UByteSerializer|null[0]
+    final val descriptor // kotlinx.serialization.internal/UByteSerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.internal/UByteSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+
+    final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlin/UByte // kotlinx.serialization.internal/UByteSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
+    final fun serialize(kotlinx.serialization.encoding/Encoder, kotlin/UByte) // kotlinx.serialization.internal/UByteSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlin.UByte){}[0]
+}
+
+final object kotlinx.serialization.internal/UIntArraySerializer : kotlinx.serialization.internal/PrimitiveArraySerializer<kotlin/UInt, kotlin/UIntArray, kotlinx.serialization.internal/UIntArrayBuilder>, kotlinx.serialization/KSerializer<kotlin/UIntArray> // kotlinx.serialization.internal/UIntArraySerializer|null[0]
+
+final object kotlinx.serialization.internal/UIntSerializer : kotlinx.serialization/KSerializer<kotlin/UInt> { // kotlinx.serialization.internal/UIntSerializer|null[0]
+    final val descriptor // kotlinx.serialization.internal/UIntSerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.internal/UIntSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+
+    final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlin/UInt // kotlinx.serialization.internal/UIntSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
+    final fun serialize(kotlinx.serialization.encoding/Encoder, kotlin/UInt) // kotlinx.serialization.internal/UIntSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlin.UInt){}[0]
+}
+
+final object kotlinx.serialization.internal/ULongArraySerializer : kotlinx.serialization.internal/PrimitiveArraySerializer<kotlin/ULong, kotlin/ULongArray, kotlinx.serialization.internal/ULongArrayBuilder>, kotlinx.serialization/KSerializer<kotlin/ULongArray> // kotlinx.serialization.internal/ULongArraySerializer|null[0]
+
+final object kotlinx.serialization.internal/ULongSerializer : kotlinx.serialization/KSerializer<kotlin/ULong> { // kotlinx.serialization.internal/ULongSerializer|null[0]
+    final val descriptor // kotlinx.serialization.internal/ULongSerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.internal/ULongSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+
+    final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlin/ULong // kotlinx.serialization.internal/ULongSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
+    final fun serialize(kotlinx.serialization.encoding/Encoder, kotlin/ULong) // kotlinx.serialization.internal/ULongSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlin.ULong){}[0]
+}
+
+final object kotlinx.serialization.internal/UShortArraySerializer : kotlinx.serialization.internal/PrimitiveArraySerializer<kotlin/UShort, kotlin/UShortArray, kotlinx.serialization.internal/UShortArrayBuilder>, kotlinx.serialization/KSerializer<kotlin/UShortArray> // kotlinx.serialization.internal/UShortArraySerializer|null[0]
+
+final object kotlinx.serialization.internal/UShortSerializer : kotlinx.serialization/KSerializer<kotlin/UShort> { // kotlinx.serialization.internal/UShortSerializer|null[0]
+    final val descriptor // kotlinx.serialization.internal/UShortSerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.internal/UShortSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+
+    final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlin/UShort // kotlinx.serialization.internal/UShortSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
+    final fun serialize(kotlinx.serialization.encoding/Encoder, kotlin/UShort) // kotlinx.serialization.internal/UShortSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlin.UShort){}[0]
+}
+
+final object kotlinx.serialization.internal/UnitSerializer : kotlinx.serialization/KSerializer<kotlin/Unit> { // kotlinx.serialization.internal/UnitSerializer|null[0]
+    final val descriptor // kotlinx.serialization.internal/UnitSerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.internal/UnitSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+
+    final fun deserialize(kotlinx.serialization.encoding/Decoder) // kotlinx.serialization.internal/UnitSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
+    final fun serialize(kotlinx.serialization.encoding/Encoder, kotlin/Unit) // kotlinx.serialization.internal/UnitSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlin.Unit){}[0]
+}
+
+final object kotlinx.serialization.internal/UuidSerializer : kotlinx.serialization/KSerializer<kotlin.uuid/Uuid> { // kotlinx.serialization.internal/UuidSerializer|null[0]
+    final val descriptor // kotlinx.serialization.internal/UuidSerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.internal/UuidSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+
+    final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlin.uuid/Uuid // kotlinx.serialization.internal/UuidSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
+    final fun serialize(kotlinx.serialization.encoding/Encoder, kotlin.uuid/Uuid) // kotlinx.serialization.internal/UuidSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlin.uuid.Uuid){}[0]
+}
+
+final val kotlinx.serialization.builtins/nullable // kotlinx.serialization.builtins/nullable|@kotlinx.serialization.KSerializer<0:0>{0§<kotlin.Any>}nullable[0]
+    final fun <#A1: kotlin/Any> (kotlinx.serialization/KSerializer<#A1>).<get-nullable>(): kotlinx.serialization/KSerializer<#A1?> // kotlinx.serialization.builtins/nullable.<get-nullable>|<get-nullable>@kotlinx.serialization.KSerializer<0:0>(){0§<kotlin.Any>}[0]
+final val kotlinx.serialization.descriptors/capturedKClass // kotlinx.serialization.descriptors/capturedKClass|@kotlinx.serialization.descriptors.SerialDescriptor{}capturedKClass[0]
+    final fun (kotlinx.serialization.descriptors/SerialDescriptor).<get-capturedKClass>(): kotlin.reflect/KClass<*>? // kotlinx.serialization.descriptors/capturedKClass.<get-capturedKClass>|<get-capturedKClass>@kotlinx.serialization.descriptors.SerialDescriptor(){}[0]
+final val kotlinx.serialization.descriptors/elementDescriptors // kotlinx.serialization.descriptors/elementDescriptors|@kotlinx.serialization.descriptors.SerialDescriptor{}elementDescriptors[0]
+    final fun (kotlinx.serialization.descriptors/SerialDescriptor).<get-elementDescriptors>(): kotlin.collections/Iterable<kotlinx.serialization.descriptors/SerialDescriptor> // kotlinx.serialization.descriptors/elementDescriptors.<get-elementDescriptors>|<get-elementDescriptors>@kotlinx.serialization.descriptors.SerialDescriptor(){}[0]
+final val kotlinx.serialization.descriptors/elementNames // kotlinx.serialization.descriptors/elementNames|@kotlinx.serialization.descriptors.SerialDescriptor{}elementNames[0]
+    final fun (kotlinx.serialization.descriptors/SerialDescriptor).<get-elementNames>(): kotlin.collections/Iterable<kotlin/String> // kotlinx.serialization.descriptors/elementNames.<get-elementNames>|<get-elementNames>@kotlinx.serialization.descriptors.SerialDescriptor(){}[0]
+final val kotlinx.serialization.descriptors/nonNullOriginal // kotlinx.serialization.descriptors/nonNullOriginal|@kotlinx.serialization.descriptors.SerialDescriptor{}nonNullOriginal[0]
+    final fun (kotlinx.serialization.descriptors/SerialDescriptor).<get-nonNullOriginal>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.descriptors/nonNullOriginal.<get-nonNullOriginal>|<get-nonNullOriginal>@kotlinx.serialization.descriptors.SerialDescriptor(){}[0]
+final val kotlinx.serialization.descriptors/nullable // kotlinx.serialization.descriptors/nullable|@kotlinx.serialization.descriptors.SerialDescriptor{}nullable[0]
+    final fun (kotlinx.serialization.descriptors/SerialDescriptor).<get-nullable>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.descriptors/nullable.<get-nullable>|<get-nullable>@kotlinx.serialization.descriptors.SerialDescriptor(){}[0]
+final val kotlinx.serialization.modules/EmptySerializersModule // kotlinx.serialization.modules/EmptySerializersModule|{}EmptySerializersModule[0]
+    final fun <get-EmptySerializersModule>(): kotlinx.serialization.modules/SerializersModule // kotlinx.serialization.modules/EmptySerializersModule.<get-EmptySerializersModule>|<get-EmptySerializersModule>(){}[0]
+
+final fun (kotlin.time/Duration.Companion).kotlinx.serialization.builtins/serializer(): kotlinx.serialization/KSerializer<kotlin.time/Duration> // kotlinx.serialization.builtins/serializer|[email protected](){}[0]
+final fun (kotlin.uuid/Uuid.Companion).kotlinx.serialization.builtins/serializer(): kotlinx.serialization/KSerializer<kotlin.uuid/Uuid> // kotlinx.serialization.builtins/serializer|[email protected](){}[0]
+final fun (kotlin/Boolean.Companion).kotlinx.serialization.builtins/serializer(): kotlinx.serialization/KSerializer<kotlin/Boolean> // kotlinx.serialization.builtins/serializer|[email protected](){}[0]
+final fun (kotlin/Byte.Companion).kotlinx.serialization.builtins/serializer(): kotlinx.serialization/KSerializer<kotlin/Byte> // kotlinx.serialization.builtins/serializer|[email protected](){}[0]
+final fun (kotlin/Char.Companion).kotlinx.serialization.builtins/serializer(): kotlinx.serialization/KSerializer<kotlin/Char> // kotlinx.serialization.builtins/serializer|[email protected](){}[0]
+final fun (kotlin/Double.Companion).kotlinx.serialization.builtins/serializer(): kotlinx.serialization/KSerializer<kotlin/Double> // kotlinx.serialization.builtins/serializer|[email protected](){}[0]
+final fun (kotlin/Float.Companion).kotlinx.serialization.builtins/serializer(): kotlinx.serialization/KSerializer<kotlin/Float> // kotlinx.serialization.builtins/serializer|[email protected](){}[0]
+final fun (kotlin/Int.Companion).kotlinx.serialization.builtins/serializer(): kotlinx.serialization/KSerializer<kotlin/Int> // kotlinx.serialization.builtins/serializer|[email protected](){}[0]
+final fun (kotlin/Long.Companion).kotlinx.serialization.builtins/serializer(): kotlinx.serialization/KSerializer<kotlin/Long> // kotlinx.serialization.builtins/serializer|[email protected](){}[0]
+final fun (kotlin/Short.Companion).kotlinx.serialization.builtins/serializer(): kotlinx.serialization/KSerializer<kotlin/Short> // kotlinx.serialization.builtins/serializer|[email protected](){}[0]
+final fun (kotlin/String.Companion).kotlinx.serialization.builtins/serializer(): kotlinx.serialization/KSerializer<kotlin/String> // kotlinx.serialization.builtins/serializer|[email protected](){}[0]
+final fun (kotlin/UByte.Companion).kotlinx.serialization.builtins/serializer(): kotlinx.serialization/KSerializer<kotlin/UByte> // kotlinx.serialization.builtins/serializer|[email protected](){}[0]
+final fun (kotlin/UInt.Companion).kotlinx.serialization.builtins/serializer(): kotlinx.serialization/KSerializer<kotlin/UInt> // kotlinx.serialization.builtins/serializer|[email protected](){}[0]
+final fun (kotlin/ULong.Companion).kotlinx.serialization.builtins/serializer(): kotlinx.serialization/KSerializer<kotlin/ULong> // kotlinx.serialization.builtins/serializer|[email protected](){}[0]
+final fun (kotlin/UShort.Companion).kotlinx.serialization.builtins/serializer(): kotlinx.serialization/KSerializer<kotlin/UShort> // kotlinx.serialization.builtins/serializer|[email protected](){}[0]
+final fun (kotlin/Unit).kotlinx.serialization.builtins/serializer(): kotlinx.serialization/KSerializer<kotlin/Unit> // kotlinx.serialization.builtins/serializer|[email protected](){}[0]
+final fun (kotlinx.serialization.descriptors/SerialDescriptor).kotlinx.serialization.internal/jsonCachedSerialNames(): kotlin.collections/Set<kotlin/String> // kotlinx.serialization.internal/jsonCachedSerialNames|jsonCachedSerialNames@kotlinx.serialization.descriptors.SerialDescriptor(){}[0]
+final fun (kotlinx.serialization.modules/SerializersModule).kotlinx.serialization.descriptors/getContextualDescriptor(kotlinx.serialization.descriptors/SerialDescriptor): kotlinx.serialization.descriptors/SerialDescriptor? // kotlinx.serialization.descriptors/getContextualDescriptor|getContextualDescriptor@kotlinx.serialization.modules.SerializersModule(kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+final fun (kotlinx.serialization.modules/SerializersModule).kotlinx.serialization.descriptors/getPolymorphicDescriptors(kotlinx.serialization.descriptors/SerialDescriptor): kotlin.collections/List<kotlinx.serialization.descriptors/SerialDescriptor> // kotlinx.serialization.descriptors/getPolymorphicDescriptors|getPolymorphicDescriptors@kotlinx.serialization.modules.SerializersModule(kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+final fun (kotlinx.serialization.modules/SerializersModule).kotlinx.serialization.modules/overwriteWith(kotlinx.serialization.modules/SerializersModule): kotlinx.serialization.modules/SerializersModule // kotlinx.serialization.modules/overwriteWith|[email protected](kotlinx.serialization.modules.SerializersModule){}[0]
+final fun (kotlinx.serialization.modules/SerializersModule).kotlinx.serialization.modules/plus(kotlinx.serialization.modules/SerializersModule): kotlinx.serialization.modules/SerializersModule // kotlinx.serialization.modules/plus|[email protected](kotlinx.serialization.modules.SerializersModule){}[0]
+final fun (kotlinx.serialization.modules/SerializersModule).kotlinx.serialization/serializer(kotlin.reflect/KClass<*>, kotlin.collections/List<kotlinx.serialization/KSerializer<*>>, kotlin/Boolean): kotlinx.serialization/KSerializer<kotlin/Any?> // kotlinx.serialization/serializer|[email protected](kotlin.reflect.KClass<*>;kotlin.collections.List<kotlinx.serialization.KSerializer<*>>;kotlin.Boolean){}[0]
+final fun (kotlinx.serialization.modules/SerializersModule).kotlinx.serialization/serializer(kotlin.reflect/KType): kotlinx.serialization/KSerializer<kotlin/Any?> // kotlinx.serialization/serializer|[email protected](kotlin.reflect.KType){}[0]
+final fun (kotlinx.serialization.modules/SerializersModule).kotlinx.serialization/serializerOrNull(kotlin.reflect/KType): kotlinx.serialization/KSerializer<kotlin/Any?>? // kotlinx.serialization/serializerOrNull|[email protected](kotlin.reflect.KType){}[0]
+final fun <#A: kotlin/Any, #B: #A?> kotlinx.serialization.builtins/ArraySerializer(kotlin.reflect/KClass<#A>, kotlinx.serialization/KSerializer<#B>): kotlinx.serialization/KSerializer<kotlin/Array<#B>> // kotlinx.serialization.builtins/ArraySerializer|ArraySerializer(kotlin.reflect.KClass<0:0>;kotlinx.serialization.KSerializer<0:1>){0§<kotlin.Any>;1§<0:0?>}[0]
+final fun <#A: kotlin/Any> (kotlin.reflect/KClass<#A>).kotlinx.serialization/serializer(): kotlinx.serialization/KSerializer<#A> // kotlinx.serialization/serializer|[email protected]<0:0>(){0§<kotlin.Any>}[0]
+final fun <#A: kotlin/Any> (kotlin.reflect/KClass<#A>).kotlinx.serialization/serializerOrNull(): kotlinx.serialization/KSerializer<#A>? // kotlinx.serialization/serializerOrNull|[email protected]<0:0>(){0§<kotlin.Any>}[0]
+final fun <#A: kotlin/Any> (kotlinx.serialization.internal/AbstractPolymorphicSerializer<#A>).kotlinx.serialization/findPolymorphicSerializer(kotlinx.serialization.encoding/CompositeDecoder, kotlin/String?): kotlinx.serialization/DeserializationStrategy<#A> // kotlinx.serialization/findPolymorphicSerializer|findPolymorphicSerializer@kotlinx.serialization.internal.AbstractPolymorphicSerializer<0:0>(kotlinx.serialization.encoding.CompositeDecoder;kotlin.String?){0§<kotlin.Any>}[0]
+final fun <#A: kotlin/Any> (kotlinx.serialization.internal/AbstractPolymorphicSerializer<#A>).kotlinx.serialization/findPolymorphicSerializer(kotlinx.serialization.encoding/Encoder, #A): kotlinx.serialization/SerializationStrategy<#A> // kotlinx.serialization/findPolymorphicSerializer|findPolymorphicSerializer@kotlinx.serialization.internal.AbstractPolymorphicSerializer<0:0>(kotlinx.serialization.encoding.Encoder;0:0){0§<kotlin.Any>}[0]
+final fun <#A: kotlin/Any> kotlinx.serialization.modules/serializersModuleOf(kotlin.reflect/KClass<#A>, kotlinx.serialization/KSerializer<#A>): kotlinx.serialization.modules/SerializersModule // kotlinx.serialization.modules/serializersModuleOf|serializersModuleOf(kotlin.reflect.KClass<0:0>;kotlinx.serialization.KSerializer<0:0>){0§<kotlin.Any>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?, #C: kotlin/Any?> kotlinx.serialization.builtins/TripleSerializer(kotlinx.serialization/KSerializer<#A>, kotlinx.serialization/KSerializer<#B>, kotlinx.serialization/KSerializer<#C>): kotlinx.serialization/KSerializer<kotlin/Triple<#A, #B, #C>> // kotlinx.serialization.builtins/TripleSerializer|TripleSerializer(kotlinx.serialization.KSerializer<0:0>;kotlinx.serialization.KSerializer<0:1>;kotlinx.serialization.KSerializer<0:2>){0§<kotlin.Any?>;1§<kotlin.Any?>;2§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?> kotlinx.serialization.builtins/MapEntrySerializer(kotlinx.serialization/KSerializer<#A>, kotlinx.serialization/KSerializer<#B>): kotlinx.serialization/KSerializer<kotlin.collections/Map.Entry<#A, #B>> // kotlinx.serialization.builtins/MapEntrySerializer|MapEntrySerializer(kotlinx.serialization.KSerializer<0:0>;kotlinx.serialization.KSerializer<0:1>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?> kotlinx.serialization.builtins/MapSerializer(kotlinx.serialization/KSerializer<#A>, kotlinx.serialization/KSerializer<#B>): kotlinx.serialization/KSerializer<kotlin.collections/Map<#A, #B>> // kotlinx.serialization.builtins/MapSerializer|MapSerializer(kotlinx.serialization.KSerializer<0:0>;kotlinx.serialization.KSerializer<0:1>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?, #B: kotlin/Any?> kotlinx.serialization.builtins/PairSerializer(kotlinx.serialization/KSerializer<#A>, kotlinx.serialization/KSerializer<#B>): kotlinx.serialization/KSerializer<kotlin/Pair<#A, #B>> // kotlinx.serialization.builtins/PairSerializer|PairSerializer(kotlinx.serialization.KSerializer<0:0>;kotlinx.serialization.KSerializer<0:1>){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.serialization/BinaryFormat).kotlinx.serialization/decodeFromHexString(kotlinx.serialization/DeserializationStrategy<#A>, kotlin/String): #A // kotlinx.serialization/decodeFromHexString|[email protected](kotlinx.serialization.DeserializationStrategy<0:0>;kotlin.String){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.serialization/BinaryFormat).kotlinx.serialization/encodeToHexString(kotlinx.serialization/SerializationStrategy<#A>, #A): kotlin/String // kotlinx.serialization/encodeToHexString|[email protected](kotlinx.serialization.SerializationStrategy<0:0>;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> kotlinx.serialization.builtins/ListSerializer(kotlinx.serialization/KSerializer<#A>): kotlinx.serialization/KSerializer<kotlin.collections/List<#A>> // kotlinx.serialization.builtins/ListSerializer|ListSerializer(kotlinx.serialization.KSerializer<0:0>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> kotlinx.serialization.builtins/SetSerializer(kotlinx.serialization/KSerializer<#A>): kotlinx.serialization/KSerializer<kotlin.collections/Set<#A>> // kotlinx.serialization.builtins/SetSerializer|SetSerializer(kotlinx.serialization.KSerializer<0:0>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> kotlinx.serialization.internal/InlinePrimitiveDescriptor(kotlin/String, kotlinx.serialization/KSerializer<#A>): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.internal/InlinePrimitiveDescriptor|InlinePrimitiveDescriptor(kotlin.String;kotlinx.serialization.KSerializer<0:0>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Enum<#A>> kotlinx.serialization.internal/createAnnotatedEnumSerializer(kotlin/String, kotlin/Array<#A>, kotlin/Array<kotlin/String?>, kotlin/Array<kotlin/Array<kotlin/Annotation>?>, kotlin/Array<kotlin/Annotation>?): kotlinx.serialization/KSerializer<#A> // kotlinx.serialization.internal/createAnnotatedEnumSerializer|createAnnotatedEnumSerializer(kotlin.String;kotlin.Array<0:0>;kotlin.Array<kotlin.String?>;kotlin.Array<kotlin.Array<kotlin.Annotation>?>;kotlin.Array<kotlin.Annotation>?){0§<kotlin.Enum<0:0>>}[0]
+final fun <#A: kotlin/Enum<#A>> kotlinx.serialization.internal/createMarkedEnumSerializer(kotlin/String, kotlin/Array<#A>, kotlin/Array<kotlin/String?>, kotlin/Array<kotlin/Array<kotlin/Annotation>?>): kotlinx.serialization/KSerializer<#A> // kotlinx.serialization.internal/createMarkedEnumSerializer|createMarkedEnumSerializer(kotlin.String;kotlin.Array<0:0>;kotlin.Array<kotlin.String?>;kotlin.Array<kotlin.Array<kotlin.Annotation>?>){0§<kotlin.Enum<0:0>>}[0]
+final fun <#A: kotlin/Enum<#A>> kotlinx.serialization.internal/createSimpleEnumSerializer(kotlin/String, kotlin/Array<#A>): kotlinx.serialization/KSerializer<#A> // kotlinx.serialization.internal/createSimpleEnumSerializer|createSimpleEnumSerializer(kotlin.String;kotlin.Array<0:0>){0§<kotlin.Enum<0:0>>}[0]
+final fun kotlinx.serialization.builtins/BooleanArraySerializer(): kotlinx.serialization/KSerializer<kotlin/BooleanArray> // kotlinx.serialization.builtins/BooleanArraySerializer|BooleanArraySerializer(){}[0]
+final fun kotlinx.serialization.builtins/ByteArraySerializer(): kotlinx.serialization/KSerializer<kotlin/ByteArray> // kotlinx.serialization.builtins/ByteArraySerializer|ByteArraySerializer(){}[0]
+final fun kotlinx.serialization.builtins/CharArraySerializer(): kotlinx.serialization/KSerializer<kotlin/CharArray> // kotlinx.serialization.builtins/CharArraySerializer|CharArraySerializer(){}[0]
+final fun kotlinx.serialization.builtins/DoubleArraySerializer(): kotlinx.serialization/KSerializer<kotlin/DoubleArray> // kotlinx.serialization.builtins/DoubleArraySerializer|DoubleArraySerializer(){}[0]
+final fun kotlinx.serialization.builtins/FloatArraySerializer(): kotlinx.serialization/KSerializer<kotlin/FloatArray> // kotlinx.serialization.builtins/FloatArraySerializer|FloatArraySerializer(){}[0]
+final fun kotlinx.serialization.builtins/IntArraySerializer(): kotlinx.serialization/KSerializer<kotlin/IntArray> // kotlinx.serialization.builtins/IntArraySerializer|IntArraySerializer(){}[0]
+final fun kotlinx.serialization.builtins/LongArraySerializer(): kotlinx.serialization/KSerializer<kotlin/LongArray> // kotlinx.serialization.builtins/LongArraySerializer|LongArraySerializer(){}[0]
+final fun kotlinx.serialization.builtins/NothingSerializer(): kotlinx.serialization/KSerializer<kotlin/Nothing> // kotlinx.serialization.builtins/NothingSerializer|NothingSerializer(){}[0]
+final fun kotlinx.serialization.builtins/ShortArraySerializer(): kotlinx.serialization/KSerializer<kotlin/ShortArray> // kotlinx.serialization.builtins/ShortArraySerializer|ShortArraySerializer(){}[0]
+final fun kotlinx.serialization.builtins/UByteArraySerializer(): kotlinx.serialization/KSerializer<kotlin/UByteArray> // kotlinx.serialization.builtins/UByteArraySerializer|UByteArraySerializer(){}[0]
+final fun kotlinx.serialization.builtins/UIntArraySerializer(): kotlinx.serialization/KSerializer<kotlin/UIntArray> // kotlinx.serialization.builtins/UIntArraySerializer|UIntArraySerializer(){}[0]
+final fun kotlinx.serialization.builtins/ULongArraySerializer(): kotlinx.serialization/KSerializer<kotlin/ULongArray> // kotlinx.serialization.builtins/ULongArraySerializer|ULongArraySerializer(){}[0]
+final fun kotlinx.serialization.builtins/UShortArraySerializer(): kotlinx.serialization/KSerializer<kotlin/UShortArray> // kotlinx.serialization.builtins/UShortArraySerializer|UShortArraySerializer(){}[0]
+final fun kotlinx.serialization.descriptors/PrimitiveSerialDescriptor(kotlin/String, kotlinx.serialization.descriptors/PrimitiveKind): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.descriptors/PrimitiveSerialDescriptor|PrimitiveSerialDescriptor(kotlin.String;kotlinx.serialization.descriptors.PrimitiveKind){}[0]
+final fun kotlinx.serialization.descriptors/SerialDescriptor(kotlin/String, kotlinx.serialization.descriptors/SerialDescriptor): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.descriptors/SerialDescriptor|SerialDescriptor(kotlin.String;kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+final fun kotlinx.serialization.descriptors/buildClassSerialDescriptor(kotlin/String, kotlin/Array<out kotlinx.serialization.descriptors/SerialDescriptor>..., kotlin/Function1<kotlinx.serialization.descriptors/ClassSerialDescriptorBuilder, kotlin/Unit> = ...): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.descriptors/buildClassSerialDescriptor|buildClassSerialDescriptor(kotlin.String;kotlin.Array<out|kotlinx.serialization.descriptors.SerialDescriptor>...;kotlin.Function1<kotlinx.serialization.descriptors.ClassSerialDescriptorBuilder,kotlin.Unit>){}[0]
+final fun kotlinx.serialization.descriptors/buildSerialDescriptor(kotlin/String, kotlinx.serialization.descriptors/SerialKind, kotlin/Array<out kotlinx.serialization.descriptors/SerialDescriptor>..., kotlin/Function1<kotlinx.serialization.descriptors/ClassSerialDescriptorBuilder, kotlin/Unit> = ...): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.descriptors/buildSerialDescriptor|buildSerialDescriptor(kotlin.String;kotlinx.serialization.descriptors.SerialKind;kotlin.Array<out|kotlinx.serialization.descriptors.SerialDescriptor>...;kotlin.Function1<kotlinx.serialization.descriptors.ClassSerialDescriptorBuilder,kotlin.Unit>){}[0]
+final fun kotlinx.serialization.descriptors/listSerialDescriptor(kotlinx.serialization.descriptors/SerialDescriptor): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.descriptors/listSerialDescriptor|listSerialDescriptor(kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+final fun kotlinx.serialization.descriptors/mapSerialDescriptor(kotlinx.serialization.descriptors/SerialDescriptor, kotlinx.serialization.descriptors/SerialDescriptor): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.descriptors/mapSerialDescriptor|mapSerialDescriptor(kotlinx.serialization.descriptors.SerialDescriptor;kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+final fun kotlinx.serialization.descriptors/serialDescriptor(kotlin.reflect/KType): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.descriptors/serialDescriptor|serialDescriptor(kotlin.reflect.KType){}[0]
+final fun kotlinx.serialization.descriptors/setSerialDescriptor(kotlinx.serialization.descriptors/SerialDescriptor): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.descriptors/setSerialDescriptor|setSerialDescriptor(kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+final fun kotlinx.serialization.internal/throwArrayMissingFieldException(kotlin/IntArray, kotlin/IntArray, kotlinx.serialization.descriptors/SerialDescriptor) // kotlinx.serialization.internal/throwArrayMissingFieldException|throwArrayMissingFieldException(kotlin.IntArray;kotlin.IntArray;kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+final fun kotlinx.serialization.internal/throwMissingFieldException(kotlin/Int, kotlin/Int, kotlinx.serialization.descriptors/SerialDescriptor) // kotlinx.serialization.internal/throwMissingFieldException|throwMissingFieldException(kotlin.Int;kotlin.Int;kotlinx.serialization.descriptors.SerialDescriptor){}[0]
+final fun kotlinx.serialization.modules/EmptySerializersModule(): kotlinx.serialization.modules/SerializersModule // kotlinx.serialization.modules/EmptySerializersModule|EmptySerializersModule(){}[0]
+final fun kotlinx.serialization/moduleThenPolymorphic(kotlinx.serialization.modules/SerializersModule, kotlin.reflect/KClass<*>): kotlinx.serialization/KSerializer<*> // kotlinx.serialization/moduleThenPolymorphic|moduleThenPolymorphic(kotlinx.serialization.modules.SerializersModule;kotlin.reflect.KClass<*>){}[0]
+final fun kotlinx.serialization/moduleThenPolymorphic(kotlinx.serialization.modules/SerializersModule, kotlin.reflect/KClass<*>, kotlin/Array<kotlinx.serialization/KSerializer<*>>): kotlinx.serialization/KSerializer<*> // kotlinx.serialization/moduleThenPolymorphic|moduleThenPolymorphic(kotlinx.serialization.modules.SerializersModule;kotlin.reflect.KClass<*>;kotlin.Array<kotlinx.serialization.KSerializer<*>>){}[0]
+final fun kotlinx.serialization/noCompiledSerializer(kotlin/String): kotlinx.serialization/KSerializer<*> // kotlinx.serialization/noCompiledSerializer|noCompiledSerializer(kotlin.String){}[0]
+final fun kotlinx.serialization/noCompiledSerializer(kotlinx.serialization.modules/SerializersModule, kotlin.reflect/KClass<*>): kotlinx.serialization/KSerializer<*> // kotlinx.serialization/noCompiledSerializer|noCompiledSerializer(kotlinx.serialization.modules.SerializersModule;kotlin.reflect.KClass<*>){}[0]
+final fun kotlinx.serialization/noCompiledSerializer(kotlinx.serialization.modules/SerializersModule, kotlin.reflect/KClass<*>, kotlin/Array<kotlinx.serialization/KSerializer<*>>): kotlinx.serialization/KSerializer<*> // kotlinx.serialization/noCompiledSerializer|noCompiledSerializer(kotlinx.serialization.modules.SerializersModule;kotlin.reflect.KClass<*>;kotlin.Array<kotlinx.serialization.KSerializer<*>>){}[0]
+final fun kotlinx.serialization/serializer(kotlin.reflect/KClass<*>, kotlin.collections/List<kotlinx.serialization/KSerializer<*>>, kotlin/Boolean): kotlinx.serialization/KSerializer<kotlin/Any?> // kotlinx.serialization/serializer|serializer(kotlin.reflect.KClass<*>;kotlin.collections.List<kotlinx.serialization.KSerializer<*>>;kotlin.Boolean){}[0]
+final fun kotlinx.serialization/serializer(kotlin.reflect/KType): kotlinx.serialization/KSerializer<kotlin/Any?> // kotlinx.serialization/serializer|serializer(kotlin.reflect.KType){}[0]
+final fun kotlinx.serialization/serializerOrNull(kotlin.reflect/KType): kotlinx.serialization/KSerializer<kotlin/Any?>? // kotlinx.serialization/serializerOrNull|serializerOrNull(kotlin.reflect.KType){}[0]
+final inline fun (kotlinx.serialization.encoding/Encoder).kotlinx.serialization.encoding/encodeCollection(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, crossinline kotlin/Function1<kotlinx.serialization.encoding/CompositeEncoder, kotlin/Unit>) // kotlinx.serialization.encoding/encodeCollection|[email protected](kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.Function1<kotlinx.serialization.encoding.CompositeEncoder,kotlin.Unit>){}[0]
+final inline fun (kotlinx.serialization.encoding/Encoder).kotlinx.serialization.encoding/encodeStructure(kotlinx.serialization.descriptors/SerialDescriptor, crossinline kotlin/Function1<kotlinx.serialization.encoding/CompositeEncoder, kotlin/Unit>) // kotlinx.serialization.encoding/encodeStructure|[email protected](kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Function1<kotlinx.serialization.encoding.CompositeEncoder,kotlin.Unit>){}[0]
+final inline fun <#A: kotlin/Any, #B: reified #A> (kotlinx.serialization.modules/PolymorphicModuleBuilder<#A>).kotlinx.serialization.modules/subclass(kotlin.reflect/KClass<#B>) // kotlinx.serialization.modules/subclass|[email protected]<0:0>(kotlin.reflect.KClass<0:1>){0§<kotlin.Any>;1§<0:0>}[0]
+final inline fun <#A: kotlin/Any, #B: reified #A> (kotlinx.serialization.modules/PolymorphicModuleBuilder<#A>).kotlinx.serialization.modules/subclass(kotlinx.serialization/KSerializer<#B>) // kotlinx.serialization.modules/subclass|[email protected]<0:0>(kotlinx.serialization.KSerializer<0:1>){0§<kotlin.Any>;1§<0:0>}[0]
+final inline fun <#A: kotlin/Any> (kotlinx.serialization.modules/SerializersModuleBuilder).kotlinx.serialization.modules/polymorphic(kotlin.reflect/KClass<#A>, kotlinx.serialization/KSerializer<#A>? = ..., kotlin/Function1<kotlinx.serialization.modules/PolymorphicModuleBuilder<#A>, kotlin/Unit> = ...) // kotlinx.serialization.modules/polymorphic|polymorphic@kotlinx.serialization.modules.SerializersModuleBuilder(kotlin.reflect.KClass<0:0>;kotlinx.serialization.KSerializer<0:0>?;kotlin.Function1<kotlinx.serialization.modules.PolymorphicModuleBuilder<0:0>,kotlin.Unit>){0§<kotlin.Any>}[0]
+final inline fun <#A: kotlin/Any?> (kotlinx.serialization.encoding/Decoder).kotlinx.serialization.encoding/decodeStructure(kotlinx.serialization.descriptors/SerialDescriptor, crossinline kotlin/Function1<kotlinx.serialization.encoding/CompositeDecoder, #A>): #A // kotlinx.serialization.encoding/decodeStructure|[email protected](kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Function1<kotlinx.serialization.encoding.CompositeDecoder,0:0>){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> (kotlinx.serialization.encoding/Encoder).kotlinx.serialization.encoding/encodeCollection(kotlinx.serialization.descriptors/SerialDescriptor, kotlin.collections/Collection<#A>, crossinline kotlin/Function3<kotlinx.serialization.encoding/CompositeEncoder, kotlin/Int, #A, kotlin/Unit>) // kotlinx.serialization.encoding/encodeCollection|[email protected](kotlinx.serialization.descriptors.SerialDescriptor;kotlin.collections.Collection<0:0>;kotlin.Function3<kotlinx.serialization.encoding.CompositeEncoder,kotlin.Int,0:0,kotlin.Unit>){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> (kotlinx.serialization/DeserializationStrategy<*>).kotlinx.serialization.internal/cast(): kotlinx.serialization/DeserializationStrategy<#A> // kotlinx.serialization.internal/cast|[email protected]<*>(){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> (kotlinx.serialization/KSerializer<*>).kotlinx.serialization.internal/cast(): kotlinx.serialization/KSerializer<#A> // kotlinx.serialization.internal/cast|[email protected]<*>(){0§<kotlin.Any?>}[0]
+final inline fun <#A: kotlin/Any?> (kotlinx.serialization/SerializationStrategy<*>).kotlinx.serialization.internal/cast(): kotlinx.serialization/SerializationStrategy<#A> // kotlinx.serialization.internal/cast|[email protected]<*>(){0§<kotlin.Any?>}[0]
+final inline fun <#A: reified kotlin/Any, #B: reified #A?> kotlinx.serialization.builtins/ArraySerializer(kotlinx.serialization/KSerializer<#B>): kotlinx.serialization/KSerializer<kotlin/Array<#B>> // kotlinx.serialization.builtins/ArraySerializer|ArraySerializer(kotlinx.serialization.KSerializer<0:1>){0§<kotlin.Any>;1§<0:0?>}[0]
+final inline fun <#A: reified kotlin/Any> (kotlinx.serialization.modules/SerializersModuleBuilder).kotlinx.serialization.modules/contextual(kotlinx.serialization/KSerializer<#A>) // kotlinx.serialization.modules/contextual|contextual@kotlinx.serialization.modules.SerializersModuleBuilder(kotlinx.serialization.KSerializer<0:0>){0§<kotlin.Any>}[0]
+final inline fun <#A: reified kotlin/Any> kotlinx.serialization.modules/serializersModuleOf(kotlinx.serialization/KSerializer<#A>): kotlinx.serialization.modules/SerializersModule // kotlinx.serialization.modules/serializersModuleOf|serializersModuleOf(kotlinx.serialization.KSerializer<0:0>){0§<kotlin.Any>}[0]
+final inline fun <#A: reified kotlin/Any?, #B: reified kotlin/Any?> kotlinx.serialization.descriptors/mapSerialDescriptor(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.descriptors/mapSerialDescriptor|mapSerialDescriptor(){0§<kotlin.Any?>;1§<kotlin.Any?>}[0]
+final inline fun <#A: reified kotlin/Any?> (kotlinx.serialization.descriptors/ClassSerialDescriptorBuilder).kotlinx.serialization.descriptors/element(kotlin/String, kotlin.collections/List<kotlin/Annotation> = ..., kotlin/Boolean = ...) // kotlinx.serialization.descriptors/element|element@kotlinx.serialization.descriptors.ClassSerialDescriptorBuilder(kotlin.String;kotlin.collections.List<kotlin.Annotation>;kotlin.Boolean){0§<kotlin.Any?>}[0]
+final inline fun <#A: reified kotlin/Any?> (kotlinx.serialization.modules/SerializersModule).kotlinx.serialization/serializer(): kotlinx.serialization/KSerializer<#A> // kotlinx.serialization/serializer|[email protected](){0§<kotlin.Any?>}[0]
+final inline fun <#A: reified kotlin/Any?> (kotlinx.serialization/BinaryFormat).kotlinx.serialization/decodeFromByteArray(kotlin/ByteArray): #A // kotlinx.serialization/decodeFromByteArray|[email protected](kotlin.ByteArray){0§<kotlin.Any?>}[0]
+final inline fun <#A: reified kotlin/Any?> (kotlinx.serialization/BinaryFormat).kotlinx.serialization/decodeFromHexString(kotlin/String): #A // kotlinx.serialization/decodeFromHexString|[email protected](kotlin.String){0§<kotlin.Any?>}[0]
+final inline fun <#A: reified kotlin/Any?> (kotlinx.serialization/BinaryFormat).kotlinx.serialization/encodeToByteArray(#A): kotlin/ByteArray // kotlinx.serialization/encodeToByteArray|[email protected](0:0){0§<kotlin.Any?>}[0]
+final inline fun <#A: reified kotlin/Any?> (kotlinx.serialization/BinaryFormat).kotlinx.serialization/encodeToHexString(#A): kotlin/String // kotlinx.serialization/encodeToHexString|[email protected](0:0){0§<kotlin.Any?>}[0]
+final inline fun <#A: reified kotlin/Any?> (kotlinx.serialization/StringFormat).kotlinx.serialization/decodeFromString(kotlin/String): #A // kotlinx.serialization/decodeFromString|[email protected](kotlin.String){0§<kotlin.Any?>}[0]
+final inline fun <#A: reified kotlin/Any?> (kotlinx.serialization/StringFormat).kotlinx.serialization/encodeToString(#A): kotlin/String // kotlinx.serialization/encodeToString|[email protected](0:0){0§<kotlin.Any?>}[0]
+final inline fun <#A: reified kotlin/Any?> kotlinx.serialization.descriptors/listSerialDescriptor(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.descriptors/listSerialDescriptor|listSerialDescriptor(){0§<kotlin.Any?>}[0]
+final inline fun <#A: reified kotlin/Any?> kotlinx.serialization.descriptors/serialDescriptor(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.descriptors/serialDescriptor|serialDescriptor(){0§<kotlin.Any?>}[0]
+final inline fun <#A: reified kotlin/Any?> kotlinx.serialization.descriptors/setSerialDescriptor(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.descriptors/setSerialDescriptor|setSerialDescriptor(){0§<kotlin.Any?>}[0]
+final inline fun <#A: reified kotlin/Any?> kotlinx.serialization/serializer(): kotlinx.serialization/KSerializer<#A> // kotlinx.serialization/serializer|serializer(){0§<kotlin.Any?>}[0]
+final inline fun kotlinx.serialization.modules/SerializersModule(kotlin/Function1<kotlinx.serialization.modules/SerializersModuleBuilder, kotlin/Unit>): kotlinx.serialization.modules/SerializersModule // kotlinx.serialization.modules/SerializersModule|SerializersModule(kotlin.Function1<kotlinx.serialization.modules.SerializersModuleBuilder,kotlin.Unit>){}[0]
+
+// Targets: [native, wasmJs, wasmWasi]
+final class <#A: kotlin/Any?, #B: kotlin/Any?> kotlinx.serialization.internal/LinkedHashMapSerializer : kotlinx.serialization.internal/MapLikeSerializer<#A, #B, kotlin.collections/Map<#A, #B>, kotlin.collections/HashMap<#A, #B>> { // kotlinx.serialization.internal/LinkedHashMapSerializer|null[0]
+    constructor <init>(kotlinx.serialization/KSerializer<#A>, kotlinx.serialization/KSerializer<#B>) // kotlinx.serialization.internal/LinkedHashMapSerializer.<init>|<init>(kotlinx.serialization.KSerializer<1:0>;kotlinx.serialization.KSerializer<1:1>){}[0]
+
+    final val descriptor // kotlinx.serialization.internal/LinkedHashMapSerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.internal/LinkedHashMapSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+}
+
+// Targets: [native, wasmJs, wasmWasi]
+final class <#A: kotlin/Any?> kotlinx.serialization.internal/LinkedHashSetSerializer : kotlinx.serialization.internal/CollectionSerializer<#A, kotlin.collections/Set<#A>, kotlin.collections/HashSet<#A>> { // kotlinx.serialization.internal/LinkedHashSetSerializer|null[0]
+    constructor <init>(kotlinx.serialization/KSerializer<#A>) // kotlinx.serialization.internal/LinkedHashSetSerializer.<init>|<init>(kotlinx.serialization.KSerializer<1:0>){}[0]
+
+    final val descriptor // kotlinx.serialization.internal/LinkedHashSetSerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.internal/LinkedHashSetSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+}
+
+// Targets: [js]
+final class <#A: kotlin/Any?, #B: kotlin/Any?> kotlinx.serialization.internal/LinkedHashMapSerializer : kotlinx.serialization.internal/MapLikeSerializer<#A, #B, kotlin.collections/Map<#A, #B>, kotlin.collections/LinkedHashMap<#A, #B>> { // kotlinx.serialization.internal/LinkedHashMapSerializer|null[0]
+    constructor <init>(kotlinx.serialization/KSerializer<#A>, kotlinx.serialization/KSerializer<#B>) // kotlinx.serialization.internal/LinkedHashMapSerializer.<init>|<init>(kotlinx.serialization.KSerializer<1:0>;kotlinx.serialization.KSerializer<1:1>){}[0]
+
+    final val descriptor // kotlinx.serialization.internal/LinkedHashMapSerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.internal/LinkedHashMapSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+}
+
+// Targets: [js]
+final class <#A: kotlin/Any?> kotlinx.serialization.internal/LinkedHashSetSerializer : kotlinx.serialization.internal/CollectionSerializer<#A, kotlin.collections/Set<#A>, kotlin.collections/LinkedHashSet<#A>> { // kotlinx.serialization.internal/LinkedHashSetSerializer|null[0]
+    constructor <init>(kotlinx.serialization/KSerializer<#A>) // kotlinx.serialization.internal/LinkedHashSetSerializer.<init>|<init>(kotlinx.serialization.KSerializer<1:0>){}[0]
+
+    final val descriptor // kotlinx.serialization.internal/LinkedHashSetSerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.internal/LinkedHashSetSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+}
diff --git a/core/build.gradle b/core/build.gradle
deleted file mode 100644
index f52837a..0000000
--- a/core/build.gradle
+++ /dev/null
@@ -1,72 +0,0 @@
-import org.jetbrains.kotlin.gradle.tasks.Kotlin2JsCompile
-
-/*
- * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-apply plugin: 'kotlin-multiplatform'
-apply plugin: 'kotlinx-serialization'
-
-apply from: rootProject.file("gradle/native-targets.gradle")
-apply from: rootProject.file("gradle/configure-source-sets.gradle")
-
-kotlin {
-    sourceSets {
-        jvmTest {
-            dependencies {
-                implementation 'io.kotlintest:kotlintest:2.0.7'
-                implementation 'com.google.guava:guava:24.1.1-jre'
-                implementation 'com.google.code.gson:gson:2.8.5'
-                implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
-            }
-        }
-    }
-}
-
-/*
- These manifest values help kotlinx.serialization compiler plugin determine if it is compatible with a given runtime library.
- Plugin reads them during compilation.
-
- Implementation-Version is used to determine whether runtime library supports a given plugin feature (e.g. value classes serialization
- in Kotlin 1.x may require runtime library version 1.y to work).
- Compiler plugin may enable or disable features by looking on Implementation-Version.
-
- Require-Kotlin-Version is used to determine whether runtime library with new features can work with old compilers.
- In ideal case, its value should always be 1.4, but some refactorings (e.g. adding a method to the Encoder interface)
- may unexpectedly break old compilers, so it is left out as a safety net. Compiler plugins, starting from 1.4 are instructed
- to reject runtime if runtime's Require-Kotlin-Version is greater then the current compiler.
- */
-tasks.withType(Jar).named(kotlin.jvm().artifactsTaskName) {
-
-    // adding the ProGuard rules to the jar
-    from(rootProject.file("rules/common.pro")) {
-        rename { "kotlinx-serialization-common.pro" }
-        into("META-INF/proguard")
-    }
-    from(rootProject.file("rules/common.pro")) {
-        rename { "kotlinx-serialization-common.pro" }
-        into("META-INF/com.android.tools/proguard")
-    }
-    from(rootProject.file("rules/common.pro")) {
-        rename { "kotlinx-serialization-common.pro" }
-        into("META-INF/com.android.tools/r8")
-    }
-    from(rootProject.file("rules/r8.pro")) {
-        rename { "kotlinx-serialization-r8.pro" }
-        into("META-INF/com.android.tools/r8")
-    }
-
-
-    manifest {
-        attributes(
-                "Implementation-Version": version,
-                "Require-Kotlin-Version": "1.4.30-M1",
-        )
-    }
-}
-
-Java9Modularity.configureJava9ModuleInfo(project)
-
-tasks.withType(org.jetbrains.kotlin.gradle.targets.js.ir.KotlinJsIrLink.class).configureEach {
-    kotlinOptions.freeCompilerArgs += "-Xwasm-enable-array-range-checks"
-}
diff --git a/core/build.gradle.kts b/core/build.gradle.kts
new file mode 100644
index 0000000..b3d885e
--- /dev/null
+++ b/core/build.gradle.kts
@@ -0,0 +1,75 @@
+import Java9Modularity.configureJava9ModuleInfo
+import org.jetbrains.kotlin.gradle.targets.js.ir.*
+
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+plugins {
+    kotlin("multiplatform")
+    alias(libs.plugins.serialization)
+
+    id("native-targets-conventions")
+    id("source-sets-conventions")
+}
+
+kotlin {
+    sourceSets {
+        jvmTest {
+            dependencies {
+                implementation(libs.kotlintest)
+                implementation(libs.guava.get24())
+                implementation(libs.gson)
+                implementation(libs.coroutines.core)
+            }
+        }
+    }
+}
+
+/*
+ These manifest values help kotlinx.serialization compiler plugin determine if it is compatible with a given runtime library.
+ Plugin reads them during compilation.
+
+ Implementation-Version is used to determine whether runtime library supports a given plugin feature (e.g. value classes serialization
+ in Kotlin 1.x may require runtime library version 1.y to work).
+ Compiler plugin may enable or disable features by looking at Implementation-Version.
+
+ Require-Kotlin-Version is used to determine whether runtime library with new features can work with old compilers.
+ In ideal case, its value should always be 1.4, but some refactorings (e.g. adding a method to the Encoder interface)
+ may unexpectedly break old compilers, so it is left out as a safety net. Compiler plugins, starting from 1.4 are instructed
+ to reject runtime if runtime's Require-Kotlin-Version is greater then the current compiler.
+ */
+tasks.withType<Jar>().named(kotlin.jvm().artifactsTaskName) {
+
+    // adding the ProGuard rules to the jar
+    from(rootDir.resolve("rules/common.pro")) {
+        rename { "kotlinx-serialization-common.pro" }
+        into("META-INF/proguard")
+    }
+    from(rootDir.resolve("rules/common.pro")) {
+        rename { "kotlinx-serialization-common.pro" }
+        into("META-INF/com.android.tools/proguard")
+    }
+    from(rootDir.resolve("rules/common.pro")) {
+        rename { "kotlinx-serialization-common.pro" }
+        into("META-INF/com.android.tools/r8")
+    }
+    from(rootDir.resolve("rules/r8.pro")) {
+        rename { "kotlinx-serialization-r8.pro" }
+        into("META-INF/com.android.tools/r8")
+    }
+
+
+    manifest {
+        attributes(
+                "Implementation-Version" to version,
+                "Require-Kotlin-Version" to "2.0.0-RC1",
+        )
+    }
+}
+
+configureJava9ModuleInfo()
+
+tasks.withType<KotlinJsIrLink>().configureEach {
+    compilerOptions.freeCompilerArgs.add("-Xwasm-enable-array-range-checks")
+}
diff --git a/core/commonMain/src/kotlinx/serialization/Annotations.kt b/core/commonMain/src/kotlinx/serialization/Annotations.kt
index 081ee82..ec1bf14 100644
--- a/core/commonMain/src/kotlinx/serialization/Annotations.kt
+++ b/core/commonMain/src/kotlinx/serialization/Annotations.kt
@@ -31,6 +31,9 @@
  * MyAnotherData.serializer() // <- returns MyAnotherDataCustomSerializer
  * ```
  *
+ * To continue generating the implementation of [KSerializer] using the plugin, specify the [KeepGeneratedSerializer] annotation.
+ * In this case, the serializer will be available via `generatedSerializer()` function, and will also be used in the heirs.
+ *
  * For annotated properties, specifying [with] parameter is mandatory and can be used to override
  * serializer on the use-site without affecting the rest of the usages:
  * ```
@@ -39,9 +42,9 @@
  *
  * @Serializable
  * class RgbExample(
- *     @Serializable(with = RgbAsHexString::class) p1: RgpPixel, // Serialize as HEX string, e.g. #FFFF00
- *     @Serializable(with = RgbAsSingleInt::class) p2: RgpPixel, // Serialize as single integer, e.g. 16711680
- *     p3: RgpPixel // Serialize as 3 short components, e.g. { "red": 255, "green": 255, "blue": 0 }
+ *     @Serializable(with = RgbAsHexString::class) p1: RgbPixel, // Serialize as HEX string, e.g. #FFFF00
+ *     @Serializable(with = RgbAsSingleInt::class) p2: RgbPixel, // Serialize as single integer, e.g. 16711680
+ *     p3: RgbPixel // Serialize as 3 short components, e.g. { "red": 255, "green": 255, "blue": 0 }
  * )
  * ```
  * In this example, each pixel will be serialized using different data representation.
@@ -64,6 +67,7 @@
  *
  * @see UseSerializers
  * @see Serializer
+ * @see KeepGeneratedSerializer
  */
 @MustBeDocumented
 @Target(AnnotationTarget.PROPERTY, AnnotationTarget.CLASS, AnnotationTarget.TYPE)
@@ -125,7 +129,7 @@
  * the name of the property, e.g. by `Json`.
  *
  * By default, [SerialDescriptor.serialName] and [SerialDescriptor.getElementName]
- * are associated with fully-qualified name of the target class and the name of the property respectively.
+ * are associated with fully qualified name of the target class and the name of the property respectively.
  * Applying this annotation changes the visible name to the given [value]:
  *
  * ```
@@ -330,13 +334,16 @@
  *
  * Automatically generated serializer is available via `generatedSerializer()` function in companion object of serializable class.
  *
- * Generated serializers allow to use custom serializers on classes from which other serializable classes are inherited.
+ * Keeping generated serializers allow to use plugin generated serializer in inheritors even if custom serializer is specified.
  *
- * Used only with the [Serializable] annotation.
+ * Used only with annotation [Serializable] with the specified argument [Serializable.with], e.g. `@Serializable(with=SomeSerializer::class)`.
  *
- * A compiler version `2.0.0` and higher is required.
+ * Annotation is not allowed on classes involved in polymorphic serialization:
+ * interfaces, sealed classes, abstract classes, classes marked by [Polymorphic].
+ *
+ * A compiler version `2.0.20` or higher is required.
  */
-@InternalSerializationApi
+@ExperimentalSerializationApi
 @Target(AnnotationTarget.CLASS)
 @Retention(AnnotationRetention.RUNTIME)
 public annotation class KeepGeneratedSerializer
diff --git a/core/commonMain/src/kotlinx/serialization/SerializationExceptions.kt b/core/commonMain/src/kotlinx/serialization/SerializationExceptions.kt
index 99f7d0a..5ca4805 100644
--- a/core/commonMain/src/kotlinx/serialization/SerializationExceptions.kt
+++ b/core/commonMain/src/kotlinx/serialization/SerializationExceptions.kt
@@ -22,12 +22,14 @@
  * It is also an established pattern to validate input in user's classes in the following manner:
  * ```
  * @Serializable
- * class Foo(...) {
+ * class User(val age: Int, val name: String) {
  *     init {
- *         required(age > 0) { ... }
+ *         require(age > 0) { ... }
  *         require(name.isNotBlank()) { ... }
  *     }
  * }
+ *
+ * Json.decodeFromString<User>("""{"age": -100, "name": ""}""") // throws IllegalArgumentException from require()
  * ```
  * While clearly being serialization error (when compromised data was deserialized),
  * Kotlin way is to throw `IllegalArgumentException` here instead of using library-specific `SerializationException`.
diff --git a/core/commonMain/src/kotlinx/serialization/Serializers.kt b/core/commonMain/src/kotlinx/serialization/Serializers.kt
index 2489be2..4e44d3d 100644
--- a/core/commonMain/src/kotlinx/serialization/Serializers.kt
+++ b/core/commonMain/src/kotlinx/serialization/Serializers.kt
@@ -189,24 +189,45 @@
     val isNullable = type.isMarkedNullable
     val typeArguments = type.arguments.map(KTypeProjection::typeOrThrow)
 
-    val cachedSerializer = if (typeArguments.isEmpty()) {
-        findCachedSerializer(rootClass, isNullable)
+    val cachedSerializer  = if (typeArguments.isEmpty()) {
+        if (rootClass.isInterface() && getContextual(rootClass) != null) {
+            // We cannot use cache because it may be contextual non-sealed interface serializer,
+            // but we cannot return result of getContextual() directly either, because rootClass
+            // can be a sealed interface as well (in that case, rootClass.serializerOrNull() should have priority over getContextual()).
+            // If we had function like KClass.isNonSealedInterface() we could optimize this place,
+            // but Native does not provide enough reflection for that. (https://youtrack.jetbrains.com/issue/KT-41339)
+            null
+        } else {
+            findCachedSerializer(rootClass, isNullable)
+        }
     } else {
-        findParametrizedCachedSerializer(rootClass, typeArguments, isNullable).getOrNull()
+        // We cannot enable cache even if the current class is non-interface, as it may have interface among type arguments
+        // and we do not want to waste time scanning them all.
+        if (hasInterfaceContextualSerializers) {
+            null
+        } else {
+            findParametrizedCachedSerializer(
+                rootClass,
+                typeArguments,
+                isNullable
+            ).getOrNull()
+        }
     }
-    cachedSerializer?.let { return it }
+
+    if (cachedSerializer != null) return cachedSerializer
 
     // slow path to find contextual serializers in serializers module
     val contextualSerializer: KSerializer<out Any?>? = if (typeArguments.isEmpty()) {
-        getContextual(rootClass)
+        rootClass.serializerOrNull()
+            ?: getContextual(rootClass)
+            ?: rootClass.polymorphicIfInterface()
     } else {
         val serializers = serializersForParameters(typeArguments, failOnMissingTypeArgSerializer) ?: return null
         // first, we look among the built-in serializers, because the parameter could be contextual
         rootClass.parametrizedSerializerOrNull(serializers) { typeArguments[0].classifier }
-            ?: getContextual(
-                rootClass,
-                serializers
-            )
+            ?: getContextual(rootClass, serializers)
+            // PolymorphicSerializer always is returned even for Interface<T>, although it rarely works as expected.
+            ?: rootClass.polymorphicIfInterface()
     }
     return contextualSerializer?.cast<Any>()?.nullable(isNullable)
 }
@@ -376,3 +397,24 @@
 ): KSerializer<*> {
     return module.getContextual(kClass, argSerializers.asList()) ?: kClass.serializerNotRegistered()
 }
+
+/**
+ * Overloads of [moduleThenPolymorphic] should never be called directly.
+ * Instead, compiler inserts calls to them when intrinsifying [serializer] function.
+ *
+ * If no request KClass is an interface, plugin performs call to [moduleThenPolymorphic] to achieve special behavior for interface serializers.
+ * (They are only serializers that have module priority over default [PolymorphicSerializer]).
+ */
+@OptIn(ExperimentalSerializationApi::class)
+@Suppress("unused")
+@PublishedApi
+internal fun moduleThenPolymorphic(module: SerializersModule, kClass: KClass<*>): KSerializer<*> {
+    return module.getContextual(kClass) ?: PolymorphicSerializer(kClass)
+}
+
+@OptIn(ExperimentalSerializationApi::class)
+@Suppress("unused")
+@PublishedApi
+internal fun moduleThenPolymorphic(module: SerializersModule, kClass: KClass<*>, argSerializers: Array<KSerializer<*>>): KSerializer<*> {
+    return module.getContextual(kClass, argSerializers.asList()) ?: PolymorphicSerializer(kClass)
+}
diff --git a/core/commonMain/src/kotlinx/serialization/SerializersCache.kt b/core/commonMain/src/kotlinx/serialization/SerializersCache.kt
index cc86e43..a4327a7 100644
--- a/core/commonMain/src/kotlinx/serialization/SerializersCache.kt
+++ b/core/commonMain/src/kotlinx/serialization/SerializersCache.kt
@@ -5,6 +5,7 @@
 package kotlinx.serialization
 
 import kotlinx.serialization.builtins.nullable
+import kotlinx.serialization.internal.*
 import kotlinx.serialization.internal.cast
 import kotlinx.serialization.internal.createCache
 import kotlinx.serialization.internal.createParametrizedCache
@@ -18,13 +19,13 @@
  * Cache for non-null non-parametrized and non-contextual serializers.
  */
 @ThreadLocal
-private val SERIALIZERS_CACHE = createCache { it.serializerOrNull() }
+internal val SERIALIZERS_CACHE = createCache { it.serializerOrNull() ?: it.polymorphicIfInterface() }
 
 /**
  * Cache for nullable non-parametrized and non-contextual serializers.
  */
 @ThreadLocal
-private val SERIALIZERS_CACHE_NULLABLE = createCache<Any?> { it.serializerOrNull()?.nullable?.cast() }
+private val SERIALIZERS_CACHE_NULLABLE = createCache<Any?> { (it.serializerOrNull() ?: it.polymorphicIfInterface())?.nullable?.cast() }
 
 /**
  * Cache for non-null parametrized and non-contextual serializers.
@@ -72,3 +73,6 @@
         PARAMETRIZED_SERIALIZERS_CACHE_NULLABLE.get(clazz, types)
     }
 }
+
+@Suppress("NOTHING_TO_INLINE")
+internal inline fun KClass<*>.polymorphicIfInterface() = if (this.isInterface()) PolymorphicSerializer(this) else null
diff --git a/core/commonMain/src/kotlinx/serialization/builtins/BuiltinSerializers.kt b/core/commonMain/src/kotlinx/serialization/builtins/BuiltinSerializers.kt
index 4bd8101..fd9af28 100644
--- a/core/commonMain/src/kotlinx/serialization/builtins/BuiltinSerializers.kt
+++ b/core/commonMain/src/kotlinx/serialization/builtins/BuiltinSerializers.kt
@@ -10,6 +10,7 @@
 import kotlin.reflect.*
 import kotlinx.serialization.descriptors.*
 import kotlin.time.Duration
+import kotlin.uuid.*
 
 /**
  * Returns a nullable serializer for the given serializer of non-null type.
@@ -216,7 +217,7 @@
 
 /**
  * Creates a serializer for [`Map<K, V>`][Map] for the given serializers for
- * its ket type [K] and value type [V].
+ * its key type [K] and value type [V].
  */
 public fun <K, V> MapSerializer(
     keySerializer: KSerializer<K>,
@@ -252,6 +253,20 @@
 public fun Duration.Companion.serializer(): KSerializer<Duration> = DurationSerializer
 
 /**
+ * Returns serializer for [Uuid].
+ * Serializer operates with a standard UUID string representation, also known as "hex-and-dash" format —
+ * [RFC 9562 section 4](https://www.rfc-editor.org/rfc/rfc9562.html#section-4).
+ *
+ * Serialization always produces lowercase string, deserialization is case-insensitive.
+ * More details can be found in the documentation of [Uuid.toString] and [Uuid.parse] functions.
+ *
+ * @see Uuid.toString
+ * @see Uuid.parse
+ */
+@ExperimentalUuidApi
+public fun Uuid.Companion.serializer(): KSerializer<Uuid> = UuidSerializer
+
+/**
  * Returns serializer for [Nothing].
  * Throws an exception when trying to encode or decode.
  *
diff --git a/core/commonMain/src/kotlinx/serialization/descriptors/SerialDescriptor.kt b/core/commonMain/src/kotlinx/serialization/descriptors/SerialDescriptor.kt
index 17fdbfe..c84bb96 100644
--- a/core/commonMain/src/kotlinx/serialization/descriptors/SerialDescriptor.kt
+++ b/core/commonMain/src/kotlinx/serialization/descriptors/SerialDescriptor.kt
@@ -19,14 +19,14 @@
  * each structure in a distinguishable and format-agnostic manner.
  *
  * ### Structure
- * Serial descriptor is identified by its [name][serialName] and consists of kind, potentially empty set of
+ * Serial descriptor is identified by its [name][serialName] and consists of a kind, potentially empty set of
  * children elements and additional metadata.
  *
  * * [serialName] uniquely identifies the descriptor (and the corresponding serializer) for non-generic types.
- *   For generic types, the actual type substitution is omitted from the string representation and the name
- *   identifies the family of the serializers without type substitutions. However, type substitution is accounted
- *   in [equals] and [hashCode] operations, meaning that descriptors of generic classes with the same name, but different type
- *   arguments, are not equal to each other.
+ *   For generic types, the actual type substitution is omitted from the string representation, and the name
+ *   identifies the family of the serializers without type substitutions. However, type substitution is accounted for
+ *   in [equals] and [hashCode] operations, meaning that descriptors of generic classes with the same name but different type
+ *   arguments are not equal to each other.
  *   [serialName] is typically used to specify the type of the target class during serialization of polymorphic and sealed
  *   classes, for observability and diagnostics.
  * * [Kind][SerialKind] defines what this descriptor represents: primitive, enum, object, collection etc.
@@ -145,12 +145,12 @@
  */
 public interface SerialDescriptor {
     /**
-     * Serial name of the descriptor that identifies pair of the associated serializer and target class.
+     * Serial name of the descriptor that identifies a pair of the associated serializer and target class.
      *
-     * For generated serializers, serial name is equal to the corresponding class's fully-qualified name
+     * For generated and default serializers, the serial name should be equal to the corresponding class's fully qualified name
      * or, if overridden, [SerialName].
-     * Custom serializers should provide a unique serial name that identify both the serializable class and
-     * the serializer itself, ignoring type arguments, if they are present.
+     * Custom serializers should provide a unique serial name that identifies both the serializable class and
+     * the serializer itself, ignoring type arguments, if they are present, for example: `my.package.LongAsTrimmedString`
      */
     @ExperimentalSerializationApi
     public val serialName: String
diff --git a/core/commonMain/src/kotlinx/serialization/descriptors/SerialDescriptors.kt b/core/commonMain/src/kotlinx/serialization/descriptors/SerialDescriptors.kt
index cb380aa..89e2cf4 100644
--- a/core/commonMain/src/kotlinx/serialization/descriptors/SerialDescriptors.kt
+++ b/core/commonMain/src/kotlinx/serialization/descriptors/SerialDescriptors.kt
@@ -224,6 +224,24 @@
     }
 
 /**
+ * Returns non-nullable serial descriptor for the type if this descriptor has been auto-generated (plugin
+ * generated descriptors) or created with `.nullable` extension on a descriptor or serializer.
+ *
+ * Otherwise, returns this.
+ *
+ * It may return nullable descriptor if this descriptor has been created manually as nullable by directly implementing SerialDescriptor interface.
+ *
+ * @see SerialDescriptor.nullable
+ * @see KSerializer.nullable
+ */
+@ExperimentalSerializationApi
+public val SerialDescriptor.nonNullOriginal: SerialDescriptor
+    get() = when (this) {
+        is SerialDescriptorForNullable -> original
+        else -> this
+    }
+
+/**
  * Builder for [SerialDescriptor] for user-defined serializers.
  *
  * Both explicit builder functions and implicit (using reified type-parameters) are present and are equivalent.
diff --git a/core/commonMain/src/kotlinx/serialization/encoding/Decoding.kt b/core/commonMain/src/kotlinx/serialization/encoding/Decoding.kt
index dc4aa2a..75bf37f 100644
--- a/core/commonMain/src/kotlinx/serialization/encoding/Decoding.kt
+++ b/core/commonMain/src/kotlinx/serialization/encoding/Decoding.kt
@@ -250,8 +250,8 @@
 
     /**
      * Decodes the value of type [T] by delegating the decoding process to the given [deserializer].
-     * For example, `decodeInt` call us equivalent to delegating integer decoding to [Int.serializer][Int.Companion.serializer]:
-     * `decodeSerializableValue(IntSerializer)`
+     * For example, `decodeInt` call is equivalent to delegating integer decoding to [Int.serializer][Int.Companion.serializer]:
+     * `decodeSerializableValue(Int.serializer())`
      */
     public fun <T : Any?> decodeSerializableValue(deserializer: DeserializationStrategy<T>): T =
         deserializer.deserialize(this)
diff --git a/core/commonMain/src/kotlinx/serialization/encoding/Encoding.kt b/core/commonMain/src/kotlinx/serialization/encoding/Encoding.kt
index 2b1dd09..76acbf9 100644
--- a/core/commonMain/src/kotlinx/serialization/encoding/Encoding.kt
+++ b/core/commonMain/src/kotlinx/serialization/encoding/Encoding.kt
@@ -272,7 +272,7 @@
 
     /**
      * Encodes the [value] of type [T] by delegating the encoding process to the given [serializer].
-     * For example, `encodeInt` call us equivalent to delegating integer encoding to [Int.serializer][Int.Companion.serializer]:
+     * For example, `encodeInt` call is equivalent to delegating integer encoding to [Int.serializer][Int.Companion.serializer]:
      * `encodeSerializableValue(Int.serializer())`
      */
     public fun <T : Any?> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) {
diff --git a/core/commonMain/src/kotlinx/serialization/internal/AbstractPolymorphicSerializer.kt b/core/commonMain/src/kotlinx/serialization/internal/AbstractPolymorphicSerializer.kt
index 26d3b5e..df53085 100644
--- a/core/commonMain/src/kotlinx/serialization/internal/AbstractPolymorphicSerializer.kt
+++ b/core/commonMain/src/kotlinx/serialization/internal/AbstractPolymorphicSerializer.kt
@@ -17,7 +17,7 @@
  *
  * By default, without special support from [Encoder], polymorphic types are serialized as list with
  * two elements: class [serial name][SerialDescriptor.serialName] (String) and the object itself.
- * Serial name equals to fully-qualified class name by default and can be changed via @[SerialName] annotation.
+ * Serial name equals to fully qualified class name by default and can be changed via @[SerialName] annotation.
  */
 @InternalSerializationApi
 @OptIn(ExperimentalSerializationApi::class)
diff --git a/core/commonMain/src/kotlinx/serialization/internal/BuiltInSerializers.kt b/core/commonMain/src/kotlinx/serialization/internal/BuiltInSerializers.kt
index 2e64a77..fbc5dc1 100644
--- a/core/commonMain/src/kotlinx/serialization/internal/BuiltInSerializers.kt
+++ b/core/commonMain/src/kotlinx/serialization/internal/BuiltInSerializers.kt
@@ -10,6 +10,7 @@
 import kotlinx.serialization.encoding.Decoder
 import kotlinx.serialization.encoding.Encoder
 import kotlin.time.Duration
+import kotlin.uuid.*
 
 
 @PublishedApi
@@ -37,3 +38,17 @@
         throw SerializationException("'kotlin.Nothing' does not have instances")
     }
 }
+
+@PublishedApi
+@ExperimentalUuidApi
+internal object UuidSerializer: KSerializer<Uuid> {
+    override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("kotlin.uuid.Uuid", PrimitiveKind.STRING)
+
+    override fun serialize(encoder: Encoder, value: Uuid) {
+        encoder.encodeString(value.toString())
+    }
+
+    override fun deserialize(decoder: Decoder): Uuid {
+        return Uuid.parse(decoder.decodeString())
+    }
+}
diff --git a/core/commonMain/src/kotlinx/serialization/internal/Enums.kt b/core/commonMain/src/kotlinx/serialization/internal/Enums.kt
index 90800d7..6d018c1 100644
--- a/core/commonMain/src/kotlinx/serialization/internal/Enums.kt
+++ b/core/commonMain/src/kotlinx/serialization/internal/Enums.kt
@@ -49,8 +49,7 @@
     }
 }
 
-@OptIn(ExperimentalSerializationApi::class)
-@InternalSerializationApi
+@PublishedApi
 internal fun <T : Enum<T>> createSimpleEnumSerializer(serialName: String, values: Array<T>): KSerializer<T> {
     return EnumSerializer(serialName, values)
 }
@@ -58,8 +57,7 @@
 /**
  * The function has a bug (#2121) and should not be used by new (1.8.20+) plugins. It is preserved for backward compatibility with previously compiled enum classes.
  */
-@OptIn(ExperimentalSerializationApi::class)
-@InternalSerializationApi
+@PublishedApi
 internal fun <T : Enum<T>> createMarkedEnumSerializer(
     serialName: String,
     values: Array<T>,
@@ -78,8 +76,7 @@
     return EnumSerializer(serialName, values, descriptor)
 }
 
-@OptIn(ExperimentalSerializationApi::class)
-@InternalSerializationApi
+@PublishedApi
 internal fun <T : Enum<T>> createAnnotatedEnumSerializer(
     serialName: String,
     values: Array<T>,
diff --git a/core/commonMain/src/kotlinx/serialization/internal/Platform.common.kt b/core/commonMain/src/kotlinx/serialization/internal/Platform.common.kt
index ef313cc..4bba9a6 100644
--- a/core/commonMain/src/kotlinx/serialization/internal/Platform.common.kt
+++ b/core/commonMain/src/kotlinx/serialization/internal/Platform.common.kt
@@ -6,7 +6,6 @@
 
 import kotlinx.serialization.*
 import kotlinx.serialization.descriptors.*
-import kotlin.native.concurrent.*
 import kotlin.reflect.*
 
 internal object InternalHexConverter {
@@ -141,6 +140,8 @@
 
 internal expect fun <T : Any> KClass<T>.compiledSerializerImpl(): KSerializer<T>?
 
+internal expect fun <T: Any> KClass<T>.isInterface(): Boolean
+
 /**
  * Create serializers cache for non-parametrized and non-contextual serializers.
  * The activity and type of cache is determined for a specific platform and a specific environment.
@@ -167,6 +168,13 @@
      * Returns cached serializer or `null` if serializer not found.
      */
     fun get(key: KClass<Any>): KSerializer<T>?
+
+    /**
+     * Use SOLELY for test purposes.
+     * May return `false` even if `get` returns value. It means that entry was computed, but not
+     *  stored (behavior for all non-JVM platforms).
+     */
+    fun isStored(key: KClass<*>): Boolean = false
 }
 
 /**
diff --git a/core/commonMain/src/kotlinx/serialization/internal/Primitives.kt b/core/commonMain/src/kotlinx/serialization/internal/Primitives.kt
index 2d9c528..2eaf5b5 100644
--- a/core/commonMain/src/kotlinx/serialization/internal/Primitives.kt
+++ b/core/commonMain/src/kotlinx/serialization/internal/Primitives.kt
@@ -11,41 +11,13 @@
 import kotlinx.serialization.builtins.*
 import kotlinx.serialization.descriptors.*
 import kotlinx.serialization.encoding.*
-import kotlin.native.concurrent.*
 import kotlin.reflect.*
 import kotlin.time.Duration
+import kotlin.uuid.*
 
-@OptIn(ExperimentalUnsignedTypes::class)
-private val BUILTIN_SERIALIZERS = mapOf(
-    String::class to String.serializer(),
-    Char::class to Char.serializer(),
-    CharArray::class to CharArraySerializer(),
-    Double::class to Double.serializer(),
-    DoubleArray::class to DoubleArraySerializer(),
-    Float::class to Float.serializer(),
-    FloatArray::class to FloatArraySerializer(),
-    Long::class to Long.serializer(),
-    LongArray::class to LongArraySerializer(),
-    ULong::class to ULong.serializer(),
-    ULongArray::class to ULongArraySerializer(),
-    Int::class to Int.serializer(),
-    IntArray::class to IntArraySerializer(),
-    UInt::class to UInt.serializer(),
-    UIntArray::class to UIntArraySerializer(),
-    Short::class to Short.serializer(),
-    ShortArray::class to ShortArraySerializer(),
-    UShort::class to UShort.serializer(),
-    UShortArray::class to UShortArraySerializer(),
-    Byte::class to Byte.serializer(),
-    ByteArray::class to ByteArraySerializer(),
-    UByte::class to UByte.serializer(),
-    UByteArray::class to UByteArraySerializer(),
-    Boolean::class to Boolean.serializer(),
-    BooleanArray::class to BooleanArraySerializer(),
-    Unit::class to Unit.serializer(),
-    Nothing::class to NothingSerializer(),
-    Duration::class to Duration.serializer()
-)
+private val BUILTIN_SERIALIZERS = initBuiltins()
+
+internal expect fun initBuiltins(): Map<KClass<*>, KSerializer<*>>
 
 internal class PrimitiveSerialDescriptor(
     override val serialName: String,
@@ -74,14 +46,13 @@
 }
 
 private fun checkName(serialName: String) {
-    val keys = BUILTIN_SERIALIZERS.keys
-    for (primitive in keys) {
-        val simpleName = primitive.simpleName!!.capitalize()
-        val qualifiedName = "kotlin.$simpleName" // KClass.qualifiedName is not supported in JS
-        if (serialName.equals(qualifiedName, ignoreCase = true) || serialName.equals(simpleName, ignoreCase = true)) {
+    val values = BUILTIN_SERIALIZERS.values
+    for (primitive in values) {
+        val primitiveName = primitive.descriptor.serialName
+        if (serialName == primitiveName) {
             throw IllegalArgumentException("""
                 The name of serial descriptor should uniquely identify associated serializer.
-                For serial name $serialName there already exist ${simpleName.capitalize()}Serializer.
+                For serial name $serialName there already exists ${primitive::class.simpleName}.
                 Please refer to SerialDescriptor documentation for additional information.
             """.trimIndent())
         }
diff --git a/core/commonMain/src/kotlinx/serialization/internal/Tagged.kt b/core/commonMain/src/kotlinx/serialization/internal/Tagged.kt
index cf71388..705cf45 100644
--- a/core/commonMain/src/kotlinx/serialization/internal/Tagged.kt
+++ b/core/commonMain/src/kotlinx/serialization/internal/Tagged.kt
@@ -299,7 +299,8 @@
         return r
     }
 
-    private val tagStack = arrayListOf<Tag>()
+    internal val tagStack: ArrayList<Tag> = arrayListOf()
+
     protected val currentTag: Tag
         get() = tagStack.last()
     protected val currentTagOrNull: Tag?
@@ -331,4 +332,10 @@
     protected open fun elementName(descriptor: SerialDescriptor, index: Int): String = descriptor.getElementName(index)
     protected open fun composeName(parentName: String, childName: String): String =
         if (parentName.isEmpty()) childName else "$parentName.$childName"
+
+
+    protected fun renderTagStack(): String {
+        return if (tagStack.isEmpty()) "$"
+        else tagStack.joinToString(separator = ".", prefix = "$.")
+    }
 }
diff --git a/core/commonMain/src/kotlinx/serialization/modules/SerializersModule.kt b/core/commonMain/src/kotlinx/serialization/modules/SerializersModule.kt
index 8a9126d..4af80ea 100644
--- a/core/commonMain/src/kotlinx/serialization/modules/SerializersModule.kt
+++ b/core/commonMain/src/kotlinx/serialization/modules/SerializersModule.kt
@@ -67,6 +67,9 @@
      */
     @ExperimentalSerializationApi
     public abstract fun dumpTo(collector: SerializersModuleCollector)
+
+    @InternalSerializationApi
+    internal abstract val hasInterfaceContextualSerializers: Boolean
 }
 
 /**
@@ -76,7 +79,14 @@
     level = DeprecationLevel.WARNING,
     replaceWith = ReplaceWith("EmptySerializersModule()"))
 @JsName("EmptySerializersModuleLegacyJs") // Compatibility with JS
-public val EmptySerializersModule: SerializersModule = SerialModuleImpl(emptyMap(), emptyMap(), emptyMap(), emptyMap(), emptyMap())
+public val EmptySerializersModule: SerializersModule = SerialModuleImpl(
+    emptyMap(),
+    emptyMap(),
+    emptyMap(),
+    emptyMap(),
+    emptyMap(),
+    false
+)
 
 /**
  * Returns a combination of two serial modules
@@ -147,7 +157,8 @@
     @JvmField val polyBase2Serializers: Map<KClass<*>, Map<KClass<*>, KSerializer<*>>>,
     private val polyBase2DefaultSerializerProvider: Map<KClass<*>, PolymorphicSerializerProvider<*>>,
     private val polyBase2NamedSerializers: Map<KClass<*>, Map<String, KSerializer<*>>>,
-    private val polyBase2DefaultDeserializerProvider: Map<KClass<*>, PolymorphicDeserializerProvider<*>>
+    private val polyBase2DefaultDeserializerProvider: Map<KClass<*>, PolymorphicDeserializerProvider<*>>,
+    internal override val hasInterfaceContextualSerializers: Boolean
 ) : SerializersModule() {
 
     override fun <T : Any> getPolymorphic(baseClass: KClass<in T>, value: T): SerializationStrategy<T>? {
@@ -197,7 +208,7 @@
         }
 
         polyBase2DefaultDeserializerProvider.forEach { (baseClass, provider) ->
-            collector.polymorphicDefaultDeserializer(baseClass as KClass<Any>, provider as (PolymorphicDeserializerProvider<out Any>))
+            collector.polymorphicDefaultDeserializer(baseClass as KClass<Any>, provider as (PolymorphicDeserializerProvider<Any>))
         }
     }
 }
diff --git a/core/commonMain/src/kotlinx/serialization/modules/SerializersModuleBuilders.kt b/core/commonMain/src/kotlinx/serialization/modules/SerializersModuleBuilders.kt
index dfb9d81..451e326 100644
--- a/core/commonMain/src/kotlinx/serialization/modules/SerializersModuleBuilders.kt
+++ b/core/commonMain/src/kotlinx/serialization/modules/SerializersModuleBuilders.kt
@@ -49,6 +49,7 @@
     private val polyBase2DefaultSerializerProvider: MutableMap<KClass<*>, PolymorphicSerializerProvider<*>> = hashMapOf()
     private val polyBase2NamedSerializers: MutableMap<KClass<*>, MutableMap<String, KSerializer<*>>> = hashMapOf()
     private val polyBase2DefaultDeserializerProvider: MutableMap<KClass<*>, PolymorphicDeserializerProvider<*>> = hashMapOf()
+    private var hasInterfaceContextualSerializers: Boolean = false
 
     /**
      * Adds [serializer] associated with given [kClass] for contextual serialization.
@@ -155,6 +156,7 @@
             }
         }
         class2ContextualProvider[forClass] = provider
+        if (forClass.isInterface()) hasInterfaceContextualSerializers = true
     }
 
     @JvmName("registerDefaultPolymorphicSerializer") // Don't mangle method name for prettier stack traces
@@ -229,7 +231,7 @@
 
     @PublishedApi
     internal fun build(): SerializersModule =
-        SerialModuleImpl(class2ContextualProvider, polyBase2Serializers, polyBase2DefaultSerializerProvider, polyBase2NamedSerializers, polyBase2DefaultDeserializerProvider)
+        SerialModuleImpl(class2ContextualProvider, polyBase2Serializers, polyBase2DefaultSerializerProvider, polyBase2NamedSerializers, polyBase2DefaultDeserializerProvider, hasInterfaceContextualSerializers)
 }
 
 /**
diff --git a/core/commonTest/src/kotlinx/serialization/CachedSerializersTest.kt b/core/commonTest/src/kotlinx/serialization/CachedSerializersTest.kt
index 212169e..39de561 100644
--- a/core/commonTest/src/kotlinx/serialization/CachedSerializersTest.kt
+++ b/core/commonTest/src/kotlinx/serialization/CachedSerializersTest.kt
@@ -61,21 +61,5 @@
     fun testAbstractSerializersAreSame() {
         assertSame(Abstract.serializer(), Abstract.serializer())
     }
-
-
-    @OptIn(ExperimentalTime::class)
-    @Test
-    fun testSerializersAreIntrinsified() = jvmOnly {
-        val m = SerializersModule {  }
-        val direct = measureTime {
-            Object.serializer()
-        }
-        val directMs = direct.inWholeMicroseconds
-        val indirect = measureTime {
-            m.serializer<Object>()
-        }
-        val indirectMs = indirect.inWholeMicroseconds
-        if (indirectMs > directMs + (directMs / 4)) error("Direct ($directMs) and indirect ($indirectMs) times are too far apart")
-    }
 }
 
diff --git a/core/commonTest/src/kotlinx/serialization/InterfaceContextualSerializerTest.kt b/core/commonTest/src/kotlinx/serialization/InterfaceContextualSerializerTest.kt
new file mode 100644
index 0000000..fbee4bf
--- /dev/null
+++ b/core/commonTest/src/kotlinx/serialization/InterfaceContextualSerializerTest.kt
@@ -0,0 +1,241 @@
+/*
+ * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.modules.*
+import kotlinx.serialization.test.*
+import kotlin.reflect.*
+import kotlin.test.*
+
+// Imagine this is a 3rd party interface
+interface IApiError {
+    val code: Int
+}
+
+@Serializable(CustomSer::class)
+interface HasCustom
+
+
+object CustomSer: KSerializer<HasCustom> {
+    override val descriptor: SerialDescriptor
+        get() = TODO("Not yet implemented")
+
+    override fun serialize(encoder: Encoder, value: HasCustom) {
+        TODO("Not yet implemented")
+    }
+
+    override fun deserialize(decoder: Decoder): HasCustom {
+        TODO("Not yet implemented")
+    }
+}
+
+@Suppress("UNCHECKED_CAST")
+class InterfaceContextualSerializerTest {
+
+    @Serializable
+    data class Box<T>(val boxed: T)
+
+    object MyApiErrorSerializer : KSerializer<IApiError> {
+        override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("IApiError", PrimitiveKind.INT)
+
+        override fun serialize(encoder: Encoder, value: IApiError) {
+            encoder.encodeInt(value.code)
+        }
+
+        override fun deserialize(decoder: Decoder): IApiError {
+            val code = decoder.decodeInt()
+            return object : IApiError {
+                override val code: Int = code
+            }
+        }
+    }
+
+    private inline fun <reified T> SerializersModule.doTest(block: (KSerializer<T>) -> Unit) {
+        block(this.serializer<T>())
+        block(this.serializer(typeOf<T>()) as KSerializer<T>)
+    }
+
+    // Native, WASM - can't retrieve serializer (no .isInterface)
+    @Test
+    fun testDefault() {
+        if (isNative() || isWasm()) return
+        assertEquals(PolymorphicKind.OPEN, serializer<IApiError>().descriptor.kind)
+        assertEquals(PolymorphicKind.OPEN, serializer(typeOf<IApiError>()).descriptor.kind)
+    }
+
+    @Test
+    fun testCustom() {
+        assertSame(CustomSer, serializer<HasCustom>())
+        assertSame(CustomSer, serializer(typeOf<HasCustom>()) as KSerializer<HasCustom>)
+    }
+
+    // JVM - intrinsics kick in
+    @Test
+    fun testContextual() {
+        val module = serializersModuleOf(IApiError::class, MyApiErrorSerializer)
+        assertSame(MyApiErrorSerializer, module.serializer(typeOf<IApiError>()) as KSerializer<IApiError>)
+        shouldFail<AssertionError>(beforeKotlin = "2.0.0", onJvm = true, onWasm = false, onNative = false, onJs = false ) {
+            assertSame(MyApiErrorSerializer, module.serializer<IApiError>())
+        }
+    }
+
+    // JVM - intrinsics kick in
+    @Test
+    fun testInsideList() {
+        val module = serializersModuleOf(IApiError::class, MyApiErrorSerializer)
+        assertEquals(MyApiErrorSerializer.descriptor, module.serializer(typeOf<List<IApiError>>()).descriptor.elementDescriptors.first())
+        shouldFail<AssertionError>(beforeKotlin = "2.0.0", onWasm = false, onNative = false, onJs = false ) {
+            assertEquals(
+                MyApiErrorSerializer.descriptor,
+                module.serializer<List<IApiError>>().descriptor.elementDescriptors.first()
+            )
+        }
+    }
+
+    @Test
+    fun testInsideBox() {
+        val module = serializersModuleOf(IApiError::class, MyApiErrorSerializer)
+        assertEquals(MyApiErrorSerializer.descriptor, module.serializer(typeOf<Box<IApiError>>()).descriptor.elementDescriptors.first())
+        shouldFail<AssertionError>(beforeKotlin = "2.0.0", onWasm = false, onNative = false, onJs = false ) {
+            assertEquals(
+                MyApiErrorSerializer.descriptor,
+                module.serializer<Box<IApiError>>().descriptor.elementDescriptors.first()
+            )
+        }
+    }
+
+    @Test
+    fun testWithNullability() {
+        val module = serializersModuleOf(IApiError::class, MyApiErrorSerializer)
+        assertEquals(MyApiErrorSerializer.nullable.descriptor, module.serializer(typeOf<IApiError?>()).descriptor)
+        shouldFail<AssertionError>(beforeKotlin = "2.0.0", onWasm = false, onNative = false, onJs = false ) {
+            assertEquals(MyApiErrorSerializer.nullable.descriptor, module.serializer<IApiError?>().descriptor)
+        }
+    }
+
+    @Test
+    fun testWithNullabilityInsideList() {
+        val module = serializersModuleOf(IApiError::class, MyApiErrorSerializer)
+        assertEquals(MyApiErrorSerializer.nullable.descriptor, module.serializer(typeOf<List<IApiError?>>()).descriptor.elementDescriptors.first())
+        shouldFail<AssertionError>(beforeKotlin = "2.0.0", onWasm = false, onNative = false, onJs = false ) {
+            assertEquals(
+                MyApiErrorSerializer.nullable.descriptor,
+                module.serializer<List<IApiError?>>().descriptor.elementDescriptors.first()
+            )
+        }
+    }
+
+    class Unrelated
+
+    object UnrelatedSerializer: KSerializer<Unrelated> {
+        override val descriptor: SerialDescriptor
+            get() = TODO("Not yet implemented")
+
+        override fun serialize(encoder: Encoder, value: Unrelated) {
+            TODO("Not yet implemented")
+        }
+
+        override fun deserialize(decoder: Decoder): Unrelated {
+            TODO("Not yet implemented")
+        }
+    }
+
+    @Test
+    fun interfaceSerializersAreCachedInsideIfModuleIsNotFilledWithInterface() = jvmOnly {
+        // Caches are implemented on JVM
+        val module = serializersModuleOf(Unrelated::class, UnrelatedSerializer)
+        val p1 = module.serializer(typeOf<List<IApiError>>())
+        assertEquals(PolymorphicKind.OPEN, p1.descriptor.elementDescriptors.first().kind)
+        val p2 = module.serializer(typeOf<List<IApiError>>())
+        assertSame(p1, p2)
+    }
+
+    @Test
+    fun interfaceSerializersAreCachedTopLevelIfModuleIsNotFilledWithInterface() = jvmOnly {
+        val module = serializersModuleOf(Unrelated::class, UnrelatedSerializer)
+        val p1 = module.serializer(typeOf<IApiError>())
+        assertEquals(PolymorphicKind.OPEN, p1.descriptor.kind)
+        val p2 = module.serializer(typeOf<IApiError>())
+        assertSame(p1, p2)
+    }
+
+    interface Parametrized<T> {
+        val param: List<T>
+    }
+
+    class PSer<T>(val tSer: KSerializer<T>): KSerializer<Parametrized<T>> {
+        override val descriptor: SerialDescriptor
+            get() = buildClassSerialDescriptor("PSer<${tSer.descriptor.serialName}>")
+
+        override fun serialize(encoder: Encoder, value: Parametrized<T>) {
+            TODO("Not yet implemented")
+        }
+
+        override fun deserialize(decoder: Decoder): Parametrized<T> {
+            TODO("Not yet implemented")
+        }
+    }
+
+    @Test
+    fun testParametrizedInterface() {
+        if (!isNative() && !isWasm()) {
+            assertEquals(PolymorphicKind.OPEN, serializer(typeOf<Parametrized<String>>()).descriptor.kind)
+        }
+        val md = SerializersModule {
+            contextual(Parametrized::class) { PSer(it[0]) }
+        }
+        assertEquals("PSer<kotlin.String>", md.serializer(typeOf<Parametrized<String>>()).descriptor.serialName)
+        shouldFail<AssertionError>(beforeKotlin = "2.0.0", onWasm = false, onNative = false, onJs = false ) {
+            assertEquals("PSer<kotlin.String>", md.serializer<Parametrized<String>>().descriptor.serialName)
+        }
+    }
+
+    @Serializable
+    sealed interface SealedI
+
+    @Test
+    fun sealedInterfacesAreNotAffected() {
+        val module = serializersModuleOf(IApiError::class, MyApiErrorSerializer)
+        module.doTest<SealedI> {
+            assertEquals(PolymorphicKind.SEALED, it.descriptor.kind)
+        }
+        module.doTest<List<SealedI>> {
+            assertEquals(PolymorphicKind.SEALED, it.descriptor.elementDescriptors.first().kind)
+        }
+        module.doTest<Box<SealedI>> {
+            assertEquals(PolymorphicKind.SEALED, it.descriptor.elementDescriptors.first().kind)
+        }
+    }
+
+    object SealedSer: KSerializer<SealedI> {
+        override val descriptor: SerialDescriptor
+            get() = PrimitiveSerialDescriptor("SealedSer", PrimitiveKind.INT)
+
+        override fun serialize(encoder: Encoder, value: SealedI) {
+            TODO("Not yet implemented")
+        }
+
+        override fun deserialize(decoder: Decoder): SealedI {
+            TODO("Not yet implemented")
+        }
+    }
+
+    @Test
+    fun sealedInterfacesAreNotOverriden() {
+        val module = serializersModuleOf(SealedI::class, SealedSer)
+        module.doTest<SealedI> {
+            assertEquals(PolymorphicKind.SEALED, it.descriptor.kind)
+        }
+        module.doTest<List<SealedI>> {
+            assertEquals(PolymorphicKind.SEALED, it.descriptor.elementDescriptors.first().kind)
+        }
+        module.doTest<Box<SealedI>> {
+            assertEquals(PolymorphicKind.SEALED, it.descriptor.elementDescriptors.first().kind)
+        }
+    }
+}
diff --git a/core/commonTest/src/kotlinx/serialization/SerialDescriptorBuilderTest.kt b/core/commonTest/src/kotlinx/serialization/SerialDescriptorBuilderTest.kt
index 1ff2a7b..7a3fa02 100644
--- a/core/commonTest/src/kotlinx/serialization/SerialDescriptorBuilderTest.kt
+++ b/core/commonTest/src/kotlinx/serialization/SerialDescriptorBuilderTest.kt
@@ -94,4 +94,44 @@
         assertTrue(descriptor.isNullable)
         assertEquals("my.Simple?", descriptor.serialName)
     }
+
+    @Test
+    fun testNonNullOriginal() {
+        listOf(
+            buildClassSerialDescriptor("my.Simple") {},
+            Boolean.serializer().descriptor,
+            String.serializer().descriptor,
+            ListSerializer(Int.serializer()).descriptor,
+        ).forEach { originalDescriptor ->
+            // Unwrapping original descriptor when it is not nullable should return the same descriptor (no-op operation)
+            assertSame(originalDescriptor.nonNullOriginal, originalDescriptor)
+
+            // Unwrapping original descriptor when it is nullable should return the original descriptor
+            assertSame(originalDescriptor.nullable.nonNullOriginal, originalDescriptor)
+        }
+
+        // Unwrapping original descriptor of a custom nullable descriptor should return the same descriptor
+        val customNullableDescriptor = CustomNullableDescriptor()
+        assertSame(customNullableDescriptor.nonNullOriginal, customNullableDescriptor)
+
+        // Unwrapping original descriptor of a nullable field should return the original non-null descriptor
+        assertSame(Type.serializer().descriptor.getElementDescriptor(0).nonNullOriginal, String.serializer().descriptor)
+
+    }
+
+    @Serializable
+    class Type(val field: String?)
+
+    private class CustomNullableDescriptor : SerialDescriptor {
+        override val isNullable: Boolean = true
+
+        override val serialName: String = "CustomNullableDescriptor"
+        override val kind: SerialKind = PrimitiveKind.STRING
+        override val elementsCount: Int = 0
+        override fun getElementName(index: Int): String = error("Should not be called")
+        override fun getElementIndex(name: String): Int = error("Should not be called")
+        override fun isElementOptional(index: Int): Boolean = error("Should not be called")
+        override fun getElementAnnotations(index: Int): List<Annotation> = error("Should not be called")
+        override fun getElementDescriptor(index: Int): SerialDescriptor = error("Should not be called")
+    }
 }
diff --git a/core/commonTest/src/kotlinx/serialization/SerialDescriptorSpecificationTest.kt b/core/commonTest/src/kotlinx/serialization/SerialDescriptorSpecificationTest.kt
index 4e888e8..bc74c72 100644
--- a/core/commonTest/src/kotlinx/serialization/SerialDescriptorSpecificationTest.kt
+++ b/core/commonTest/src/kotlinx/serialization/SerialDescriptorSpecificationTest.kt
@@ -204,9 +204,10 @@
 
     @Test
     fun testCustomPrimitiveDescriptor() {
+        // It is allowed to have a custom descriptor with 'Int' or 'int', but not 'kotlin.Int'.
         assertFailsWith<IllegalArgumentException> { PrimitiveSerialDescriptor("kotlin.Int", PrimitiveKind.INT) }
-        assertFailsWith<IllegalArgumentException> { PrimitiveSerialDescriptor("Int", PrimitiveKind.INT) }
-        assertFailsWith<IllegalArgumentException> { PrimitiveSerialDescriptor("int", PrimitiveKind.INT) }
+        assertEquals("Int", PrimitiveSerialDescriptor("Int", PrimitiveKind.INT).serialName)
+        assertEquals("int", PrimitiveSerialDescriptor("int", PrimitiveKind.INT).serialName)
     }
 
     private fun checkPrimitiveDescriptor(type: String, descriptor: SerialDescriptor) {
diff --git a/core/commonTest/src/kotlinx/serialization/SerializersLookupNamedCompanionTest.kt b/core/commonTest/src/kotlinx/serialization/SerializersLookupNamedCompanionTest.kt
index 65324c4..8611d34 100644
--- a/core/commonTest/src/kotlinx/serialization/SerializersLookupNamedCompanionTest.kt
+++ b/core/commonTest/src/kotlinx/serialization/SerializersLookupNamedCompanionTest.kt
@@ -88,13 +88,7 @@
                 serializer(typeOf<SealedInterface>()).descriptor.toString()
             )
         }
-
-        // should fail because annotation @NamedCompanion will be placed again by the compilation plugin
-        // and they both will be placed into @Container annotation - thus they will be invisible to the runtime
-        shouldFail<SerializationException>(sinceKotlin = "1.9.20", onJs = false, onNative = false, onWasm = false) {
-            serializer(typeOf<SealedInterfaceWithExplicitAnnotation>())
-        }
     }
 
 
-}
\ No newline at end of file
+}
diff --git a/core/jsMain/src/kotlinx/serialization/internal/Platform.kt b/core/jsMain/src/kotlinx/serialization/internal/Platform.kt
index 6bd6339..e423afc 100644
--- a/core/jsMain/src/kotlinx/serialization/internal/Platform.kt
+++ b/core/jsMain/src/kotlinx/serialization/internal/Platform.kt
@@ -5,7 +5,10 @@
 package kotlinx.serialization.internal
 
 import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
 import kotlin.reflect.*
+import kotlin.time.*
+import kotlin.uuid.*
 
 internal actual fun <T> Array<T>.getChecked(index: Int): T {
     if (index !in indices) throw IndexOutOfBoundsException("Index $index out of bounds $indices")
@@ -19,10 +22,12 @@
 
 internal actual fun <T : Any> KClass<T>.compiledSerializerImpl(): KSerializer<T>? =
     this.constructSerializerForGivenTypeArgs() ?: (
-        if (this === Nothing::class) NothingSerializer // Workaround for KT-51333
+        if (this === Nothing::class) NothingSerializer // .js throws an exception for Nothing
         else this.js.asDynamic().Companion?.serializer()
         ) as? KSerializer<T>
 
+internal actual fun <T: Any> KClass<T>.isInterface(): Boolean = isInterface
+
 internal actual fun <T> createCache(factory: (KClass<*>) -> KSerializer<T>?): SerializerCache<T> {
     return object: SerializerCache<T> {
         override fun get(key: KClass<Any>): KSerializer<T>? {
@@ -56,7 +61,6 @@
         when {
             assocObject is KSerializer<*> -> assocObject as KSerializer<T>
             assocObject is SerializerFactory -> assocObject.serializer(*args) as KSerializer<T>
-            this.isInterface -> PolymorphicSerializer(this)
             else -> null
         }
     } catch (e: dynamic) {
@@ -70,5 +74,42 @@
  *
  * Should be eventually replaced with compiler intrinsics
  */
-private val KClass<*>.isInterface
-    get(): Boolean = js.asDynamic().`$metadata$`?.kind == "interface"
+private val KClass<*>.isInterface: Boolean
+    get(): Boolean {
+        // .js throws an exception for Nothing
+        if (this === Nothing::class) return false
+        return js.asDynamic().`$metadata$`?.kind == "interface"
+    }
+
+@OptIn(ExperimentalUnsignedTypes::class, ExperimentalUuidApi::class, ExperimentalSerializationApi::class)
+internal actual fun initBuiltins(): Map<KClass<*>, KSerializer<*>> = mapOf(
+    String::class to String.serializer(),
+    Char::class to Char.serializer(),
+    CharArray::class to CharArraySerializer(),
+    Double::class to Double.serializer(),
+    DoubleArray::class to DoubleArraySerializer(),
+    Float::class to Float.serializer(),
+    FloatArray::class to FloatArraySerializer(),
+    Long::class to Long.serializer(),
+    LongArray::class to LongArraySerializer(),
+    ULong::class to ULong.serializer(),
+    ULongArray::class to ULongArraySerializer(),
+    Int::class to Int.serializer(),
+    IntArray::class to IntArraySerializer(),
+    UInt::class to UInt.serializer(),
+    UIntArray::class to UIntArraySerializer(),
+    Short::class to Short.serializer(),
+    ShortArray::class to ShortArraySerializer(),
+    UShort::class to UShort.serializer(),
+    UShortArray::class to UShortArraySerializer(),
+    Byte::class to Byte.serializer(),
+    ByteArray::class to ByteArraySerializer(),
+    UByte::class to UByte.serializer(),
+    UByteArray::class to UByteArraySerializer(),
+    Boolean::class to Boolean.serializer(),
+    BooleanArray::class to BooleanArraySerializer(),
+    Unit::class to Unit.serializer(),
+    Nothing::class to NothingSerializer(),
+    Duration::class to Duration.serializer(),
+    Uuid::class to Uuid.serializer()
+)
diff --git a/core/jvmMain/src/kotlinx/serialization/SerializersJvm.kt b/core/jvmMain/src/kotlinx/serialization/SerializersJvm.kt
index b2d8da7..2e9e2d0 100644
--- a/core/jvmMain/src/kotlinx/serialization/SerializersJvm.kt
+++ b/core/jvmMain/src/kotlinx/serialization/SerializersJvm.kt
@@ -168,7 +168,7 @@
 ): KSerializer<T>? {
     jClass.constructSerializerForGivenTypeArgs(*typeArgumentsSerializers.toTypedArray())?.let { return it }
     val kClass = jClass.kotlin
-    return kClass.builtinSerializerOrNull() ?: getContextual(kClass, typeArgumentsSerializers)
+    return kClass.builtinSerializerOrNull() ?: getContextual(kClass, typeArgumentsSerializers) ?: if (jClass.isInterface) PolymorphicSerializer(jClass.kotlin) else null
 }
 
 @OptIn(ExperimentalSerializationApi::class)
diff --git a/core/jvmMain/src/kotlinx/serialization/internal/Caching.kt b/core/jvmMain/src/kotlinx/serialization/internal/Caching.kt
index 191b30c..8ab4c85 100644
--- a/core/jvmMain/src/kotlinx/serialization/internal/Caching.kt
+++ b/core/jvmMain/src/kotlinx/serialization/internal/Caching.kt
@@ -52,6 +52,10 @@
             .getOrSet(key.java) { CacheEntry(compute(key)) }
             .serializer
     }
+
+    override fun isStored(key: KClass<*>): Boolean {
+        return classValue.isStored(key.java)
+    }
 }
 
 /**
@@ -85,6 +89,11 @@
         return ref.getOrSetWithLock { factory() }
     }
 
+    fun isStored(key: Class<*>): Boolean {
+        val ref = get(key)
+        return ref.reference.get() != null
+    }
+
 }
 
 /**
@@ -134,6 +143,10 @@
             CacheEntry(compute(key))
         }.serializer
     }
+
+    override fun isStored(key: KClass<*>): Boolean {
+        return cache.containsKey(key.java)
+    }
 }
 
 
diff --git a/core/jvmMain/src/kotlinx/serialization/internal/Platform.kt b/core/jvmMain/src/kotlinx/serialization/internal/Platform.kt
index 72ec9ea..b838a0f 100644
--- a/core/jvmMain/src/kotlinx/serialization/internal/Platform.kt
+++ b/core/jvmMain/src/kotlinx/serialization/internal/Platform.kt
@@ -5,8 +5,11 @@
 package kotlinx.serialization.internal
 
 import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
 import java.lang.reflect.*
 import kotlin.reflect.*
+import kotlin.time.*
+import kotlin.uuid.*
 
 @Suppress("NOTHING_TO_INLINE")
 internal actual inline fun <T> Array<T>.getChecked(index: Int): T {
@@ -18,6 +21,8 @@
     return get(index)
 }
 
+internal actual fun <T: Any> KClass<T>.isInterface(): Boolean = java.isInterface
+
 internal actual fun <T : Any> KClass<T>.compiledSerializerImpl(): KSerializer<T>? =
     this.constructSerializerForGivenTypeArgs()
 
@@ -39,8 +44,6 @@
     if (isEnum && isNotAnnotated()) {
         return createEnumSerializer()
     }
-    // Fall-through if the serializer is not found -- lookup on companions (for sealed interfaces) or fallback to polymorphic if applicable
-    if (isInterface) interfaceSerializer()?.let { return it }
     // Search for serializer defined on companion object.
     val serializer = invokeSerializerOnDefaultCompanion<T>(this, *args)
     if (serializer != null) return serializer
@@ -105,19 +108,6 @@
     return false
 }
 
-private fun <T: Any> Class<T>.interfaceSerializer(): KSerializer<T>? {
-    /*
-     * Interfaces are @Polymorphic by default.
-     * Check if it has no annotations or `@Serializable(with = PolymorphicSerializer::class)`,
-     * otherwise bailout.
-     */
-    val serializable = getAnnotation(Serializable::class.java)
-    if (serializable == null || serializable.with == PolymorphicSerializer::class) {
-        return PolymorphicSerializer(this.kotlin)
-    }
-    return null
-}
-
 private fun <T : Any> invokeSerializerOnDefaultCompanion(jClass: Class<*>, vararg args: KSerializer<Any?>): KSerializer<T>? {
     val companion = jClass.companionOrNull("Companion") ?: return null
     return invokeSerializerOnCompanion(companion, *args)
@@ -171,3 +161,53 @@
 }
 
 internal actual fun isReferenceArray(rootClass: KClass<Any>): Boolean = rootClass.java.isArray
+
+@OptIn(ExperimentalSerializationApi::class)
+internal actual fun initBuiltins(): Map<KClass<*>, KSerializer<*>> = buildMap {
+    // Standard classes are always present
+    put(String::class, String.serializer())
+    put(Char::class, Char.serializer())
+    put(CharArray::class, CharArraySerializer())
+    put(Double::class, Double.serializer())
+    put(DoubleArray::class, DoubleArraySerializer())
+    put(Float::class, Float.serializer())
+    put(FloatArray::class, FloatArraySerializer())
+    put(Long::class, Long.serializer())
+    put(LongArray::class, LongArraySerializer())
+    put(ULong::class, ULong.serializer())
+    put(Int::class, Int.serializer())
+    put(IntArray::class, IntArraySerializer())
+    put(UInt::class, UInt.serializer())
+    put(Short::class, Short.serializer())
+    put(ShortArray::class, ShortArraySerializer())
+    put(UShort::class, UShort.serializer())
+    put(Byte::class, Byte.serializer())
+    put(ByteArray::class, ByteArraySerializer())
+    put(UByte::class, UByte.serializer())
+    put(Boolean::class, Boolean.serializer())
+    put(BooleanArray::class, BooleanArraySerializer())
+    put(Unit::class, Unit.serializer())
+    put(Nothing::class, NothingSerializer())
+
+    // Duration is a stable class, but may be missing in very old stdlibs
+    loadSafe { put(Duration::class, Duration.serializer()) }
+
+    // Experimental types that may be missing
+    @OptIn(ExperimentalUnsignedTypes::class) run {
+        loadSafe { put(ULongArray::class, ULongArraySerializer()) }
+        loadSafe { put(UIntArray::class, UIntArraySerializer()) }
+        loadSafe { put(UShortArray::class, UShortArraySerializer()) }
+        loadSafe { put(UByteArray::class, UByteArraySerializer()) }
+    }
+    @OptIn(ExperimentalUuidApi::class)
+    loadSafe { put(Uuid::class, Uuid.serializer()) }
+}
+
+// Reference classes in [block] ignoring any exceptions related to class loading
+private inline fun loadSafe(block: () -> Unit) {
+    try {
+        block()
+    } catch (_: NoClassDefFoundError) {
+    } catch (_: ClassNotFoundException) {
+    }
+}
diff --git a/core/jvmTest/src/kotlinx/serialization/CachingTest.kt b/core/jvmTest/src/kotlinx/serialization/CachingTest.kt
index b146c92..634fb00 100644
--- a/core/jvmTest/src/kotlinx/serialization/CachingTest.kt
+++ b/core/jvmTest/src/kotlinx/serialization/CachingTest.kt
@@ -6,9 +6,9 @@
 
 import kotlinx.serialization.internal.*
 import kotlinx.serialization.modules.*
-import org.junit.Test
 import kotlin.reflect.*
 import kotlin.test.*
+import kotlin.test.Test
 
 class CachingTest {
     @Test
@@ -43,4 +43,38 @@
 
         assertEquals(1, factoryCalled)
     }
+
+    @Serializable
+    class Target
+
+    @Test
+    fun testJvmIntrinsics() {
+        val ser1 = Target.serializer()
+        assertFalse(SERIALIZERS_CACHE.isStored(Target::class), "Cache shouldn't have values before call to serializer<T>()")
+        val ser2 = serializer<Target>()
+        assertFalse(
+            SERIALIZERS_CACHE.isStored(Target::class),
+            "Serializer for Target::class is stored in the cache, which means that runtime lookup was performed and call to serializer<Target> was not intrinsified." +
+                "Check that compiler plugin intrinsics are enabled and working correctly."
+        )
+        val ser3 = serializer(typeOf<Target>())
+        assertTrue(SERIALIZERS_CACHE.isStored(Target::class), "Serializer should be stored in cache after typeOf-based lookup")
+    }
+
+    @Serializable
+    class Target2
+
+    inline fun <reified T : Any> indirect(): KSerializer<T> = serializer<T>()
+
+    @Test
+    fun testJvmIntrinsicsIndirect() {
+        val ser1 = Target2.serializer()
+        assertFalse(SERIALIZERS_CACHE.isStored(Target2::class), "Cache shouldn't have values before call to serializer<T>()")
+        val ser2 = indirect<Target2>()
+        assertFalse(
+            SERIALIZERS_CACHE.isStored(Target2::class),
+            "Serializer for Target2::class is stored in the cache, which means that runtime lookup was performed and call to serializer<Target2> was not intrinsified." +
+                "Check that compiler plugin intrinsics are enabled and working correctly."
+        )
+    }
 }
diff --git a/core/jvmTest/src/kotlinx/serialization/InterfaceContextualSerializerTestJvm.kt b/core/jvmTest/src/kotlinx/serialization/InterfaceContextualSerializerTestJvm.kt
new file mode 100644
index 0000000..d409df2
--- /dev/null
+++ b/core/jvmTest/src/kotlinx/serialization/InterfaceContextualSerializerTestJvm.kt
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.modules.*
+import kotlinx.serialization.test.*
+import kotlinx.serialization.test.shouldFail
+import org.junit.Test
+import kotlin.reflect.*
+import kotlin.test.*
+
+interface JApiError {
+    val code: Int
+}
+
+class InterfaceContextualSerializerTestJvm {
+    object MyApiErrorSerializer : KSerializer<JApiError> {
+        override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("JApiError", PrimitiveKind.INT)
+
+        override fun serialize(encoder: Encoder, value: JApiError) {
+            encoder.encodeInt(value.code)
+        }
+
+        override fun deserialize(decoder: Decoder): JApiError {
+            val code = decoder.decodeInt()
+            return object : JApiError {
+                override val code: Int = code
+            }
+        }
+    }
+
+    @Test
+    fun testDefault() {
+        assertEquals(PolymorphicKind.OPEN, serializer(typeTokenOf<JApiError>()).descriptor.kind)
+    }
+
+    @Suppress("UNCHECKED_CAST")
+    @Test
+    fun testContextual() {
+        val module = serializersModuleOf(JApiError::class, MyApiErrorSerializer)
+        assertSame(MyApiErrorSerializer, module.serializer(typeTokenOf<JApiError>()) as KSerializer<JApiError>)
+    }
+
+    @Test
+    fun testInsideList() {
+        val module = serializersModuleOf(JApiError::class, MyApiErrorSerializer)
+        assertSame(MyApiErrorSerializer.descriptor, module.serializer(typeTokenOf<List<JApiError>>()).descriptor.elementDescriptors.first())
+    }
+
+    interface Parametrized<T> {
+        val param: List<T>
+    }
+
+    class PSer<T>(val tSer: KSerializer<T>): KSerializer<Parametrized<T>> {
+        override val descriptor: SerialDescriptor
+            get() = buildClassSerialDescriptor("PSer<${tSer.descriptor.serialName}>")
+
+        override fun serialize(encoder: Encoder, value: Parametrized<T>) {
+            TODO("Not yet implemented")
+        }
+
+        override fun deserialize(decoder: Decoder): Parametrized<T> {
+            TODO("Not yet implemented")
+        }
+    }
+
+    @Test
+    fun testParametrizedInterface() {
+        assertEquals(PolymorphicKind.OPEN, serializer(typeTokenOf<Parametrized<String>>()).descriptor.kind)
+        val md = SerializersModule {
+            contextual(Parametrized::class) { PSer(it[0]) }
+        }
+        assertEquals("PSer<kotlin.String>", md.serializer(typeTokenOf<Parametrized<String>>()).descriptor.serialName)
+    }
+}
diff --git a/core/jvmTest/src/kotlinx/serialization/features/SerializerJvmSpecificTest.kt b/core/jvmTest/src/kotlinx/serialization/features/SerializerJvmSpecificTest.kt
index 76a304b..5ffd7ac 100644
--- a/core/jvmTest/src/kotlinx/serialization/features/SerializerJvmSpecificTest.kt
+++ b/core/jvmTest/src/kotlinx/serialization/features/SerializerJvmSpecificTest.kt
@@ -19,9 +19,11 @@
 
     interface ImplicitInterface
 
+    @Suppress("SERIALIZER_TYPE_INCOMPATIBLE")
     @Serializable(with = PolymorphicSerializer::class)
     interface ExplicitInterface
 
+    @Suppress("SERIALIZER_TYPE_INCOMPATIBLE")
     @Serializable
     class Holder(
         val iif: ImplicitInterface,
diff --git a/core/nativeMain/src/kotlinx/serialization/internal/Platform.kt b/core/nativeMain/src/kotlinx/serialization/internal/Platform.kt
index 2c91769..c2e9fdd 100644
--- a/core/nativeMain/src/kotlinx/serialization/internal/Platform.kt
+++ b/core/nativeMain/src/kotlinx/serialization/internal/Platform.kt
@@ -5,7 +5,10 @@
 package kotlinx.serialization.internal
 
 import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
 import kotlin.reflect.*
+import kotlin.time.*
+import kotlin.uuid.*
 
 @Suppress("NOTHING_TO_INLINE")
 internal actual inline fun <T> Array<T>.getChecked(index: Int): T {
@@ -41,6 +44,7 @@
 internal actual fun <T : Any> KClass<T>.compiledSerializerImpl(): KSerializer<T>? =
     this.constructSerializerForGivenTypeArgs()
 
+internal actual fun <T: Any> KClass<T>.isInterface(): Boolean = false // we do not know, but also PolymorphicSerializer is never returned on Native for interfaces
 
 internal actual fun <T> createCache(factory: (KClass<*>) -> KSerializer<T>?): SerializerCache<T> {
     return object: SerializerCache<T> {
@@ -70,3 +74,36 @@
 private fun <T> arrayOfAnyNulls(size: Int): Array<T> = arrayOfNulls<Any>(size) as Array<T>
 
 internal actual fun isReferenceArray(rootClass: KClass<Any>): Boolean = rootClass == Array::class
+
+@OptIn(ExperimentalUnsignedTypes::class, ExperimentalUuidApi::class, ExperimentalSerializationApi::class)
+internal actual fun initBuiltins(): Map<KClass<*>, KSerializer<*>> = mapOf(
+    String::class to String.serializer(),
+    Char::class to Char.serializer(),
+    CharArray::class to CharArraySerializer(),
+    Double::class to Double.serializer(),
+    DoubleArray::class to DoubleArraySerializer(),
+    Float::class to Float.serializer(),
+    FloatArray::class to FloatArraySerializer(),
+    Long::class to Long.serializer(),
+    LongArray::class to LongArraySerializer(),
+    ULong::class to ULong.serializer(),
+    ULongArray::class to ULongArraySerializer(),
+    Int::class to Int.serializer(),
+    IntArray::class to IntArraySerializer(),
+    UInt::class to UInt.serializer(),
+    UIntArray::class to UIntArraySerializer(),
+    Short::class to Short.serializer(),
+    ShortArray::class to ShortArraySerializer(),
+    UShort::class to UShort.serializer(),
+    UShortArray::class to UShortArraySerializer(),
+    Byte::class to Byte.serializer(),
+    ByteArray::class to ByteArraySerializer(),
+    UByte::class to UByte.serializer(),
+    UByteArray::class to UByteArraySerializer(),
+    Boolean::class to Boolean.serializer(),
+    BooleanArray::class to BooleanArraySerializer(),
+    Unit::class to Unit.serializer(),
+    Nothing::class to NothingSerializer(),
+    Duration::class to Duration.serializer(),
+    Uuid::class to Uuid.serializer()
+)
diff --git a/core/wasmMain/src/kotlinx/serialization/internal/Platform.kt b/core/wasmMain/src/kotlinx/serialization/internal/Platform.kt
index 310df02..ecfee8e 100644
--- a/core/wasmMain/src/kotlinx/serialization/internal/Platform.kt
+++ b/core/wasmMain/src/kotlinx/serialization/internal/Platform.kt
@@ -5,7 +5,10 @@
 package kotlinx.serialization.internal
 
 import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
 import kotlin.reflect.*
+import kotlin.time.*
+import kotlin.uuid.*
 
 @Suppress("NOTHING_TO_INLINE")
 internal actual inline fun <T> Array<T>.getChecked(index: Int): T {
@@ -40,6 +43,7 @@
 internal actual fun <T : Any> KClass<T>.compiledSerializerImpl(): KSerializer<T>? =
     this.constructSerializerForGivenTypeArgs()
 
+internal actual fun <T: Any> KClass<T>.isInterface(): Boolean = false // we do not know, but also PolymorphicSerializer is never returned on WASM for interfaces
 
 internal actual fun <T> createCache(factory: (KClass<*>) -> KSerializer<T>?): SerializerCache<T> {
     return object: SerializerCache<T> {
@@ -59,4 +63,37 @@
 
 internal actual fun <T : Any, E : T?> ArrayList<E>.toNativeArrayImpl(eClass: KClass<T>): Array<E> = toTypedArray()
 
-internal actual fun isReferenceArray(rootClass: KClass<Any>): Boolean = rootClass == Array::class
\ No newline at end of file
+internal actual fun isReferenceArray(rootClass: KClass<Any>): Boolean = rootClass == Array::class
+
+@OptIn(ExperimentalUnsignedTypes::class, ExperimentalUuidApi::class, ExperimentalSerializationApi::class)
+internal actual fun initBuiltins(): Map<KClass<*>, KSerializer<*>> = mapOf(
+    String::class to String.serializer(),
+    Char::class to Char.serializer(),
+    CharArray::class to CharArraySerializer(),
+    Double::class to Double.serializer(),
+    DoubleArray::class to DoubleArraySerializer(),
+    Float::class to Float.serializer(),
+    FloatArray::class to FloatArraySerializer(),
+    Long::class to Long.serializer(),
+    LongArray::class to LongArraySerializer(),
+    ULong::class to ULong.serializer(),
+    ULongArray::class to ULongArraySerializer(),
+    Int::class to Int.serializer(),
+    IntArray::class to IntArraySerializer(),
+    UInt::class to UInt.serializer(),
+    UIntArray::class to UIntArraySerializer(),
+    Short::class to Short.serializer(),
+    ShortArray::class to ShortArraySerializer(),
+    UShort::class to UShort.serializer(),
+    UShortArray::class to UShortArraySerializer(),
+    Byte::class to Byte.serializer(),
+    ByteArray::class to ByteArraySerializer(),
+    UByte::class to UByte.serializer(),
+    UByteArray::class to UByteArraySerializer(),
+    Boolean::class to Boolean.serializer(),
+    BooleanArray::class to BooleanArraySerializer(),
+    Unit::class to Unit.serializer(),
+    Nothing::class to NothingSerializer(),
+    Duration::class to Duration.serializer(),
+    Uuid::class to Uuid.serializer()
+)
diff --git a/docs/basic-serialization.md b/docs/basic-serialization.md
index 96e7098..ce98f49 100644
--- a/docs/basic-serialization.md
+++ b/docs/basic-serialization.md
@@ -450,6 +450,7 @@
 
 ```kotlin
 @Serializable
+@OptIn(ExperimentalSerializationApi::class) // EncodeDefault is an experimental annotation for now
 data class Project(
     val name: String,
     @EncodeDefault val language: String = "Kotlin"
@@ -462,6 +463,7 @@
 ```kotlin
 
 @Serializable
+@OptIn(ExperimentalSerializationApi::class) // EncodeDefault is an experimental annotation for now
 data class User(
     val name: String,
     @EncodeDefault(EncodeDefault.Mode.NEVER) val projects: List<Project> = emptyList()
diff --git a/docs/building.md b/docs/building.md
index e9d00a0..313bf8a 100644
--- a/docs/building.md
+++ b/docs/building.md
@@ -5,15 +5,15 @@
 To build Kotlin Serialization JDK version 11 or higher is required. Make sure this is your default JDK (`JAVA_HOME` is set accordingly).
 This is needed to compile the `module-info` file included for JPMS support.
 
-In case you are determined to use different JDK version, or experience problems with JPMS you can turn off compilation of modules
+In case you are determined to use a different JDK version or experience problems with JPMS, you can turn off compilation of module-info files
 completely with `disableJPMS` property: add `disableJPMS=true` to gradle.properties or `-PdisableJPMS` to Gradle CLI invocation.
 
 ## Runtime library
 
 Kotlin Serialization runtime library itself is a [multiplatform](http://kotlinlang.org/docs/reference/multiplatform.html) project.
-To build library from the source and run all tests, use `./gradlew build`. Corresponding platform tasks like `jvmTest`, `jsTest`, `nativeTest` and so on are also available.
+To build the library from the source and run all tests, use `./gradlew build`. Corresponding platform tasks like `jvmTest,` `jsTest,` `macosArm64Test`, and so on are also available.
 
-Project can be opened in in Intellij IDEA without additional prerequisites.
+The project can be opened in IntelliJ IDEA without additional prerequisites.
 In case you want to work with Protobuf tests, you may need to run `./gradlew generateTestProto` beforehand.
 
 
@@ -30,19 +30,16 @@
 }
 ```
 
-Note that by default, only one Native target is built (the one that is the current host, e.g. `macosX64` on Intel Mac machines, `linuxX64` on linux machines, etc).
-To compile and publish all Native artifacts, not only the host one, use Gradle property `native.deploy=true`.
-
-To use snapshot version of compiler (if you have built and install it from sources), use flag `-Pbootstrap`.
+To use snapshot version of compiler (if you have built and installed it from sources), use flag `-Pbootstrap`.
 If you have built both Kotlin and Kotlin/Native compilers, set `KONAN_LOCAL_DIST` environment property to the path with Kotlin/Native distribution
 (usually `kotlin-native/dist` folder inside Kotlin project).
 
-`master` and `dev` branches of library should be binary compatible with latest released compiler plugin. In case you want to test some new features from other branches,
+The `master` and `dev` branches of the library should be binary compatible with the latest released compiler plugin. In case you want to test some new features from other branches,
 which are still in development and may not be compatible in terms of bytecode produced by plugin, you'll need to build the plugin by yourself.
 
 ## Compiler plugin
 
-Compiler plugin for Gradle/Maven and IntelliJ plugin, starting from Kotlin 1.3, are embedded into the Kotlin compiler. 
+Compiler plugins for Gradle/Maven and IntelliJ plugin, starting from Kotlin 1.3, are embedded into the Kotlin compiler. 
 
 Sources and steps to build it are located [here](https://github.com/JetBrains/kotlin/tree/master/plugins/kotlinx-serialization).
-In short, you'll just need to run `./gradlew dist install` to get `1.x.255-SNAPSHOT` versions of Kotlin compiler, stdlib and serialization plugins in the Maven local repository.
+In short, you'll just need to run `./gradlew dist install` to get `2.x.255-SNAPSHOT` versions of Kotlin compiler, stdlib, and serialization plugins in the Maven local repository.
diff --git a/docs/builtin-classes.md b/docs/builtin-classes.md
index 8671dc7..f36b8d6 100644
--- a/docs/builtin-classes.md
+++ b/docs/builtin-classes.md
@@ -351,8 +351,8 @@
 
 ### Unit and singleton objects
 
-The Kotlin builtin `Unit` type is also serializable. 
-`Unit` is a Kotlin [singleton object](https://kotlinlang.org/docs/tutorials/kotlin-for-py/objects-and-companion-objects.html), 
+The Kotlin builtin [Unit](https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-unit/) type is also serializable. 
+`Unit` is a Kotlin [singleton object](https://kotlinlang.org/docs/object-declarations.html#object-declarations-overview), 
 and is handled equally with other Kotlin objects.
 
 Conceptually, a singleton is a class with only one instance, meaning that state does not define the object, 
@@ -383,7 +383,7 @@
 <!--- TEST -->
 
 > Serialization of objects is format specific. Other formats may represent objects differently, 
-> e.g. using their fully-qualified names.
+> e.g. using their fully qualified names.
 
 ### Duration
 
@@ -427,7 +427,7 @@
 ``` 
 > You can get the full code [here](../guide/example/example-builtin-13.kt).
 
-When encoding, the serializer for the `Nothing` was not used
+When encoding, the serializer for `Nothing` was not used
 
 ```text
 {"value":42}
diff --git a/docs/formats.md b/docs/formats.md
index 3fcbf9c..3076012 100644
--- a/docs/formats.md
+++ b/docs/formats.md
@@ -13,11 +13,18 @@
 * [CBOR (experimental)](#cbor-experimental)
   * [Ignoring unknown keys](#ignoring-unknown-keys)
   * [Byte arrays and CBOR data types](#byte-arrays-and-cbor-data-types)
+  * [Definite vs. Indefinite Length Encoding](#definite-vs-indefinite-length-encoding)
+  * [Tags and Labels](#tags-and-labels)
+  * [Arrays](#arrays)
+  * [Custom CBOR-specific Serializers](#custom-cbor-specific-serializers)
 * [ProtoBuf (experimental)](#protobuf-experimental)
   * [Field numbers](#field-numbers)
   * [Integer types](#integer-types)
   * [Lists as repeated fields](#lists-as-repeated-fields)
   * [Packed fields](#packed-fields)
+  * [Oneof field (experimental)](#oneof-field-experimental)
+    * [Usage](#usage)
+    * [Alternative](#alternative)
   * [ProtoBuf schema generator (experimental)](#protobuf-schema-generator-experimental)
 * [Properties (experimental)](#properties-experimental)
 * [Custom formats (experimental)](#custom-formats-experimental)
@@ -58,6 +65,7 @@
 @Serializable
 data class Project(val name: String, val language: String)
 
+@OptIn(ExperimentalSerializationApi::class)
 fun main() {
     val data = Project("kotlinx.serialization", "Kotlin") 
     val bytes = Cbor.encodeToByteArray(data)   
@@ -109,13 +117,14 @@
 -->
 
 ```kotlin
-val format = Cbor { ignoreUnknownKeys = true }
-
 @Serializable
 data class Project(val name: String)
 
+@OptIn(ExperimentalSerializationApi::class)
 fun main() {
-    val data = format.decodeFromHexString<Project>(
+  val format = Cbor { ignoreUnknownKeys = true }
+  
+  val data = format.decodeFromHexString<Project>(
         "bf646e616d65756b6f746c696e782e73657269616c697a6174696f6e686c616e6775616765664b6f746c696eff"
     )
     println(data)
@@ -161,6 +170,8 @@
 
 By default, Kotlin `ByteArray` instances are encoded as **major type 4**.
 When **major type 2** is desired, then the [`@ByteString`][ByteString] annotation can be used.
+Moreover, the `alwaysUseByteString` configuration switch allows for globally preferring **major type 2** without needing
+to annotate every `ByteArray` in a class hierarchy.
 
 <!--- INCLUDE
 import kotlinx.serialization.*
@@ -174,12 +185,14 @@
 
 ```kotlin
 @Serializable
+@OptIn(ExperimentalSerializationApi::class)
 data class Data(
     @ByteString
     val type2: ByteArray, // CBOR Major type 2
     val type4: ByteArray  // CBOR Major type 4
-)        
+)
 
+@OptIn(ExperimentalSerializationApi::class)
 fun main() {
     val data = Data(byteArrayOf(1, 2, 3, 4), byteArrayOf(5, 6, 7, 8)) 
     val bytes = Cbor.encodeToByteArray(data)   
@@ -218,6 +231,90 @@
    FF            # primitive(*)
 ```
 
+### Definite vs. Indefinite Length Encoding
+CBOR supports two encodings for maps and arrays: definite and indefinite length encoding. kotlinx.serialization defaults
+to the latter, which means that a map's or array's number of elements is not encoded, but instead a terminating byte is
+appended after the last element.
+Definite length encoding, on the other hand, omits this terminating byte, but instead prepends number of elements
+to the contents of a map or array. The `useDefiniteLengthEncoding` configuration switch allows for toggling between the
+two modes of encoding.
+
+
+### Tags and Labels
+
+CBOR allows for optionally defining *tags* for properties and their values. These tags are encoded into the resulting
+byte string to transport additional information
+(see [RFC 8949 Tagging of Items](https://datatracker.ietf.org/doc/html/rfc8949#name-tagging-of-items) for more info).
+The  [`@KeyTags`](Tags.kt) and [`@ValueTags`](Tags.kt) annotations can be used to define such tags while
+writing and verifying such tags can be toggled using the  `encodeKeyTags`, `encodeValueTags`, `verifyKeyTags`, and
+`verifyValueTags` configuration switches respectively.
+In addition, it is possible to directly declare classes to always be tagged.
+This then applies to all instances of such a tagged class, regardless of whether they are used as values in a list
+or when they are used as a property in another class.
+Forcing objects to always be tagged in such a manner is accomplished by the [`@ObjectTags`](Tags.kt) annotation,
+which works just as `ValueTags`, but for class definitions.
+When serializing, `ObjectTags` will always be encoded directly before to the data of the tagged object, i.e. a
+value-tagged property of an object-tagged type will have the value tags preceding the object tags.
+Writing and verifying object tags can be toggled using the `encodeObjectTags` and `verifyObjectTags` configuration
+switches. Note that verifying only value tags can result in some data with superfluous tags to still deserialize
+successfully, since in this case - by definition - only a partial validation of tags happens.
+Well-known tags are specified in [`CborTag`](Tags.kt).
+
+In addition, CBOR supports keys of all types which work just as `SerialName`s.
+COSE restricts this again to strings and numbers and calls these restricted map keys *labels*. String labels can be
+assigned by using `@SerialName`, while number labels can be assigned using the [`@CborLabel`](CborLabel.kt) annotation.
+The `preferCborLabelsOverNames` configuration switch can be used to prefer number labels over SerialNames in case both
+are present for a property. This duality allows for compact representation of a type when serialized to CBOR, while
+keeping expressive diagnostic names when serializing to JSON.
+
+A predefined Cbor instance (in addition to the default [`Cbor.Default`](Cbor.kt) one) is available, adhering to COSE
+encoding requirements as [`Cbor.CoseCompliant`](Cbor.kt). This instance uses definite length encoding,
+encodes and verifies all tags and prefers labels to serial names.
+
+### Arrays
+
+Classes may be serialized as a CBOR Array (major type 4) instead of a CBOR Map (major type 5).
+
+Example usage:
+
+```
+@Serializable
+data class DataClass(
+    val alg: Int,
+    val kid: String?
+)
+
+Cbor.encodeToByteArray(DataClass(alg = -7, kid = null))
+```
+
+will normally produce a Cbor map: bytes `0xa263616c6726636b6964f6`, or in diagnostic notation:
+
+```
+A2           # map(2)
+   63        # text(3)
+      616C67 # "alg"
+   26        # negative(6)
+   63        # text(3)
+      6B6964 # "kid"
+   F6        # primitive(22)
+```
+
+When annotated with `@CborArray`, serialization of the same object will produce a Cbor array: bytes `0x8226F6`, or in diagnostic notation:
+
+```
+82    # array(2)
+   26 # negative(6)
+   F6 # primitive(22)
+```
+This may be used to encode COSE structures, see [RFC 9052 2. Basic COSE Structure](https://www.rfc-editor.org/rfc/rfc9052#section-2).
+
+
+### Custom CBOR-specific Serializers
+Cbor encoders and decoders implement the interfaces [CborEncoder](CborEncoder.kt) and [CborDecoder](CborDecoder.kt), respectively.
+These interfaces contain a single property, `cbor`, exposing the current CBOR serialization configuration.
+This enables custom cbor-specific serializers to reuse the current `Cbor` instance to produce embedded byte arrays or
+react to configuration settings such as `preferCborLabelsOverNames` or `useDefiniteLengthEncoding`, for example.
+
 ## ProtoBuf (experimental)
 
 [Protocol Buffers](https://developers.google.com/protocol-buffers) is a language-neutral binary format that normally
@@ -245,6 +342,7 @@
 @Serializable
 data class Project(val name: String, val language: String)
 
+@OptIn(ExperimentalSerializationApi::class)
 fun main() {
     val data = Project("kotlinx.serialization", "Kotlin") 
     val bytes = ProtoBuf.encodeToByteArray(data)   
@@ -287,6 +385,7 @@
 -->
 
 ```kotlin    
+@OptIn(ExperimentalSerializationApi::class)
 @Serializable
 data class Project(
     @ProtoNumber(1)
@@ -295,6 +394,7 @@
     val language: String
 )
 
+@OptIn(ExperimentalSerializationApi::class)
 fun main() {
     val data = Project("kotlinx.serialization", "Kotlin") 
     val bytes = ProtoBuf.encodeToByteArray(data)   
@@ -339,6 +439,7 @@
 -->
 
 ```kotlin    
+@OptIn(ExperimentalSerializationApi::class)
 @Serializable
 class Data(
     @ProtoType(ProtoIntegerType.DEFAULT)
@@ -349,6 +450,7 @@
     val c: Int
 )
 
+@OptIn(ExperimentalSerializationApi::class)
 fun main() {
     val data = Data(1, -2, 3) 
     println(ProtoBuf.encodeToByteArray(data).toAsciiHexString())
@@ -404,6 +506,7 @@
     val b: List<Int> = emptyList()
 )
 
+@OptIn(ExperimentalSerializationApi::class)
 fun main() {
     val data = Data(listOf(1, 2, 3), listOf())
     val bytes = ProtoBuf.encodeToByteArray(data)
@@ -435,6 +538,110 @@
 Per the [format description](https://developers.google.com/protocol-buffers/docs/encoding#packed) the parser ignores
 the annotation, but rather reads list in either packed or repeated format.
 
+### Oneof field (experimental)
+
+Kotlin Serialization `ProtoBuf` format supports [oneof](https://protobuf.dev/programming-guides/proto2/#oneof) fields
+basing on the [Polymorphism](polymorphism.md) functionality.
+
+#### Usage
+
+Given a protobuf message defined like:
+
+```proto
+message Data {
+    required string name = 1;
+    oneof phone {
+        string home_phone = 2;
+        string work_phone = 3;
+    }
+}
+```
+
+You can define a kotlin class semantically equal to this message by following these steps:
+
+* Declare a sealed interface or abstract class, to represent of the `oneof` group, called *the oneof interface*. In our example, oneof interface is `IPhoneType`.
+* Declare a Kotlin class as usual to represent the whole message (`class Data` in our example). In this class, add the property with oneof interface type, annotated with `@ProtoOneOf`. Do not use `@ProtoNumber` for that property.
+* Declare subclasses for oneof interface, one per each oneof group element. Each class must have **exactly one property** with the corresponding oneof element type. In our example, these classes are `HomePhone` and `WorkPhone`.
+* Annotate properties in subclasses with `@ProtoNumber`, according to original `oneof` definition. In our example, `val number: String` in `HomePhone` has `@ProtoNumber(2)` annotation, because of field `string home_phone = 2;` in `oneof phone`.
+
+<!--- INCLUDE
+import kotlinx.serialization.*
+import kotlinx.serialization.protobuf.*
+-->
+
+```kotlin
+// The outer class
+@OptIn(ExperimentalSerializationApi::class)
+@Serializable
+data class Data(
+    @ProtoNumber(1) val name: String,
+    @ProtoOneOf val phone: IPhoneType?,
+)
+
+// The oneof interface
+@Serializable sealed interface IPhoneType
+
+// Message holder for home_phone
+@OptIn(ExperimentalSerializationApi::class)
+@Serializable @JvmInline value class HomePhone(@ProtoNumber(2) val number: String): IPhoneType
+
+// Message holder for work_phone. Can also be a value class, but we leave it as `data` to demonstrate that both variants can be used.
+@OptIn(ExperimentalSerializationApi::class)
+@Serializable data class WorkPhone(@ProtoNumber(3) val number: String): IPhoneType
+
+@OptIn(ExperimentalSerializationApi::class)
+fun main() {
+  val dataTom = Data("Tom", HomePhone("123"))
+  val stringTom = ProtoBuf.encodeToHexString(dataTom)
+  val dataJerry = Data("Jerry", WorkPhone("789"))
+  val stringJerry = ProtoBuf.encodeToHexString(dataJerry)
+  println(stringTom)
+  println(stringJerry)
+  println(ProtoBuf.decodeFromHexString<Data>(stringTom))
+  println(ProtoBuf.decodeFromHexString<Data>(stringJerry))
+}
+```
+
+> You can get the full code [here](../guide/example/example-formats-08.kt).
+
+```text
+0a03546f6d1203313233
+0a054a657272791a03373839
+Data(name=Tom, phone=HomePhone(number=123))
+Data(name=Jerry, phone=WorkPhone(number=789))
+```
+
+<!--- TEST -->
+
+In [ProtoBuf diagnostic mode](https://protogen.marcgravell.com/decode) the first 2 lines in the output are equivalent to
+
+```
+Field #1: 0A String Length = 3, Hex = 03, UTF8 = "Tom" Field #2: 12 String Length = 3, Hex = 03, UTF8 = "123"
+Field #1: 0A String Length = 5, Hex = 05, UTF8 = "Jerry" Field #3: 1A String Length = 3, Hex = 03, UTF8 = "789"
+```
+
+You should note that each group of `oneof` types should be tied to exactly one data class, and it is better not to reuse it in
+another data class. Otherwise, you may get id conflicts or `IllegalArgumentException` in runtime.
+
+#### Alternative
+
+You don't always need to apply the `@ProtoOneOf` form in your class for messages with `oneof` fields, if this class is only used for deserialization.
+
+For example, the following class:
+
+```
+@Serializable  
+data class Data2(  
+    @ProtoNumber(1) val name: String,  
+    @ProtoNumber(2) val homeNumber: String? = null,  
+    @ProtoNumber(3) val workNumber: String? = null,  
+)  
+```
+
+is also compatible with the `message Data` given above, which means the same input can be deserialized into it instead of `Data` — in case you don't want to deal with sealed hierarchies.
+
+But please note that there are no exclusivity checks. This means that if an instance of `Data2` has both (or none) `homeNumber` and `workNumber` as non-null values and is serialized to protobuf, it no longer complies with the original schema. If you send such data to another parser, one of the fields may be omitted, leading to an unknown issue.
+
 ### ProtoBuf schema generator (experimental)
 
 As mentioned above, when working with protocol buffers you usually use a ".proto" file and a code generator for your
@@ -461,13 +668,15 @@
     val description: String?,
     val department: String = "QA"
 )
+
+@OptIn(ExperimentalSerializationApi::class)
 fun main() {
   val descriptors = listOf(SampleData.serializer().descriptor)
   val schemas = ProtoBufSchemaGenerator.generateSchemaText(descriptors)
   println(schemas)
 }
 ```
-> You can get the full code [here](../guide/example/example-formats-08.kt).
+> You can get the full code [here](../guide/example/example-formats-09.kt).
 
 Which would output as follows.
 
@@ -475,7 +684,7 @@
 syntax = "proto2";
 
 
-// serial name 'example.exampleFormats08.SampleData'
+// serial name 'example.exampleFormats09.SampleData'
 message SampleData {
   required int64 amount = 1;
   optional string description = 2;
@@ -512,6 +721,7 @@
 @Serializable
 class User(val name: String)
 
+@OptIn(ExperimentalSerializationApi::class)
 fun main() {
     val data = Project("kotlinx.serialization",  User("kotlin"))
     val map = Properties.encodeToMap(data)
@@ -519,7 +729,7 @@
 }
 ```      
 
-> You can get the full code [here](../guide/example/example-formats-09.kt).
+> You can get the full code [here](../guide/example/example-formats-10.kt).
 
 The resulting map has dot-separated keys representing keys of the nested objects.
 
@@ -544,7 +754,8 @@
 
 Let us start with a trivial format implementation that encodes the data into a single list of primitive
 constituent objects in the order they were written in the source code. To start, we implement a simple [Encoder] by
-overriding `encodeValue` in [AbstractEncoder].
+overriding `encodeValue` in [AbstractEncoder]. Since encoders are intended to be consumed by other parts of application,
+it is recommended to propagate the `@ExperimentalSerializationApi` annotation instead of opting-in.
 
 <!--- INCLUDE
 import kotlinx.serialization.*
@@ -554,6 +765,7 @@
 -->
 
 ```kotlin
+@ExperimentalSerializationApi
 class ListEncoder : AbstractEncoder() {
     val list = mutableListOf<Any>()
 
@@ -569,6 +781,7 @@
 and returns a list.
 
 ```kotlin
+@ExperimentalSerializationApi
 fun <T> encodeToList(serializer: SerializationStrategy<T>, value: T): List<Any> {
     val encoder = ListEncoder()
     encoder.encodeSerializableValue(serializer, value)
@@ -581,6 +794,7 @@
 the appropriate [KSerializer] instance for the actual type.
 
 ```kotlin 
+@ExperimentalSerializationApi
 inline fun <reified T> encodeToList(value: T) = encodeToList(serializer(), value)
 ```                  
 
@@ -593,13 +807,14 @@
 @Serializable
 data class User(val name: String)
 
+@OptIn(ExperimentalSerializationApi::class)
 fun main() {
     val data = Project("kotlinx.serialization",  User("kotlin"), 9000)
     println(encodeToList(data))
 }
 ```                                    
 
-> You can get the full code [here](../guide/example/example-formats-10.kt).
+> You can get the full code [here](../guide/example/example-formats-11.kt).
 
 As a result, we got all the primitive values in our object graph visited and put into a list
 in _serial_ order.
@@ -621,6 +836,7 @@
 import kotlinx.serialization.encoding.*
 import kotlinx.serialization.modules.*
 
+@ExperimentalSerializationApi
 class ListEncoder : AbstractEncoder() {
     val list = mutableListOf<Any>()
 
@@ -631,12 +847,14 @@
     }
 }
 
+@ExperimentalSerializationApi
 fun <T> encodeToList(serializer: SerializationStrategy<T>, value: T): List<Any> {
     val encoder = ListEncoder()
     encoder.encodeSerializableValue(serializer, value)
     return encoder.list
 }
 
+@ExperimentalSerializationApi
 inline fun <reified T> encodeToList(value: T) = encodeToList(serializer(), value)
 -->
 
@@ -648,10 +866,11 @@
   in the `elementIndex` variable. See 
   the [Hand-written composite serializer](serializers.md#hand-written-composite-serializer) section 
   on how it ends up being used.
-* [beginStructure][Decoder.beginStructure] &mdash; returns a new instance of the `ListDecoder`, so that
+* [beginStructure][Decoder.beginStructure] &mdash; returns a new instance of `ListDecoder`, so that
   each structure that is being recursively decoded keeps track of its own `elementIndex` state separately.  
 
 ```kotlin
+@ExperimentalSerializationApi
 class ListDecoder(val list: ArrayDeque<Any>) : AbstractDecoder() {
     private var elementIndex = 0
 
@@ -672,11 +891,13 @@
 A couple of convenience functions for decoding.
 
 ```kotlin
+@ExperimentalSerializationApi
 fun <T> decodeFromList(list: List<Any>, deserializer: DeserializationStrategy<T>): T {
     val decoder = ListDecoder(ArrayDeque(list))
     return decoder.decodeSerializableValue(deserializer)
 }
 
+@ExperimentalSerializationApi
 inline fun <reified T> decodeFromList(list: List<Any>): T = decodeFromList(list, serializer())
 ```
 
@@ -692,6 +913,7 @@
 -->
 
 ```kotlin    
+@OptIn(ExperimentalSerializationApi::class)
 fun main() {
     val data = Project("kotlinx.serialization",  User("kotlin"), 9000)
     val list = encodeToList(data)
@@ -701,7 +923,7 @@
 }
 ```
 
-> You can get the full code [here](../guide/example/example-formats-11.kt).
+> You can get the full code [here](../guide/example/example-formats-12.kt).
 
 Now we can convert a list of primitives back to an object tree.
 
@@ -729,6 +951,7 @@
 import kotlinx.serialization.encoding.*
 import kotlinx.serialization.modules.*
 
+@ExperimentalSerializationApi
 class ListEncoder : AbstractEncoder() {
     val list = mutableListOf<Any>()
 
@@ -739,16 +962,19 @@
     }
 }
 
+@ExperimentalSerializationApi
 fun <T> encodeToList(serializer: SerializationStrategy<T>, value: T): List<Any> {
     val encoder = ListEncoder()
     encoder.encodeSerializableValue(serializer, value)
     return encoder.list
 }
 
+@ExperimentalSerializationApi
 inline fun <reified T> encodeToList(value: T) = encodeToList(serializer(), value)
 -->
 
 ```kotlin
+@ExperimentalSerializationApi
 class ListDecoder(val list: ArrayDeque<Any>) : AbstractDecoder() {
     private var elementIndex = 0
 
@@ -770,11 +996,13 @@
 
 <!--- INCLUDE
 
+@ExperimentalSerializationApi
 fun <T> decodeFromList(list: List<Any>, deserializer: DeserializationStrategy<T>): T {
     val decoder = ListDecoder(ArrayDeque(list))
     return decoder.decodeSerializableValue(deserializer)
 }
 
+@ExperimentalSerializationApi
 inline fun <reified T> decodeFromList(list: List<Any>): T = decodeFromList(list, serializer())
 
 @Serializable
@@ -783,6 +1011,7 @@
 @Serializable
 data class User(val name: String)
 
+@OptIn(ExperimentalSerializationApi::class)
 fun main() {
     val data = Project("kotlinx.serialization",  User("kotlin"), 9000)
     val list = encodeToList(data)
@@ -792,7 +1021,7 @@
 }
 -->
 
-> You can get the full code [here](../guide/example/example-formats-12.kt).
+> You can get the full code [here](../guide/example/example-formats-13.kt).
 
 <!--- TEST 
 [kotlinx.serialization, kotlin, 9000]
@@ -816,6 +1045,7 @@
 -->
 
 ```kotlin
+@ExperimentalSerializationApi
 class ListEncoder : AbstractEncoder() {
     val list = mutableListOf<Any>()
 
@@ -834,12 +1064,14 @@
 
 <!--- INCLUDE
 
+@ExperimentalSerializationApi
 fun <T> encodeToList(serializer: SerializationStrategy<T>, value: T): List<Any> {
     val encoder = ListEncoder()
     encoder.encodeSerializableValue(serializer, value)
     return encoder.list
 }
 
+@ExperimentalSerializationApi
 inline fun <reified T> encodeToList(value: T) = encodeToList(serializer(), value)
 -->
 
@@ -849,6 +1081,7 @@
 > The formats that store collection size in advance have to return `true` from `decodeSequentially`.
 
 ```kotlin
+@ExperimentalSerializationApi
 class ListDecoder(val list: ArrayDeque<Any>, var elementsCount: Int = 0) : AbstractDecoder() {
     private var elementIndex = 0
 
@@ -873,11 +1106,13 @@
 
 <!--- INCLUDE
 
+@ExperimentalSerializationApi
 fun <T> decodeFromList(list: List<Any>, deserializer: DeserializationStrategy<T>): T {
     val decoder = ListDecoder(ArrayDeque(list))
     return decoder.decodeSerializableValue(deserializer)
 }
 
+@ExperimentalSerializationApi
 inline fun <reified T> decodeFromList(list: List<Any>): T = decodeFromList(list, serializer())
 -->
 
@@ -890,6 +1125,7 @@
 @Serializable
 data class User(val name: String)
 
+@OptIn(ExperimentalSerializationApi::class)
 fun main() {
     val data = Project("kotlinx.serialization",  listOf(User("kotlin"), User("jetbrains")), 9000)
     val list = encodeToList(data)
@@ -899,7 +1135,7 @@
 }
 ```
 
-> You can get the full code [here](../guide/example/example-formats-13.kt).
+> You can get the full code [here](../guide/example/example-formats-14.kt).
 
 We see the size of the list added to the result, letting the decoder know where to stop. 
 
@@ -921,6 +1157,7 @@
 import kotlinx.serialization.encoding.*
 import kotlinx.serialization.modules.*
 
+@ExperimentalSerializationApi
 class ListEncoder : AbstractEncoder() {
     val list = mutableListOf<Any>()
 
@@ -946,14 +1183,17 @@
 <!--- INCLUDE
 }
 
+@ExperimentalSerializationApi
 fun <T> encodeToList(serializer: SerializationStrategy<T>, value: T): List<Any> {
     val encoder = ListEncoder()
     encoder.encodeSerializableValue(serializer, value)
     return encoder.list
 }
 
+@ExperimentalSerializationApi
 inline fun <reified T> encodeToList(value: T) = encodeToList(serializer(), value)
 
+@ExperimentalSerializationApi
 class ListDecoder(val list: ArrayDeque<Any>, var elementsCount: Int = 0) : AbstractDecoder() {
     private var elementIndex = 0
     
@@ -984,11 +1224,13 @@
 <!--- INCLUDE
 }
 
+@ExperimentalSerializationApi
 fun <T> decodeFromList(list: List<Any>, deserializer: DeserializationStrategy<T>): T {
     val decoder = ListDecoder(ArrayDeque(list))
     return decoder.decodeSerializableValue(deserializer)
 }
 
+@ExperimentalSerializationApi
 inline fun <reified T> decodeFromList(list: List<Any>): T = decodeFromList(list, serializer())
 -->
 
@@ -1001,6 +1243,7 @@
 @Serializable
 data class User(val name: String)
 
+@OptIn(ExperimentalSerializationApi::class)
 fun main() {
     val data = Project("kotlinx.serialization",  User("kotlin") , null)
     val list = encodeToList(data)
@@ -1011,7 +1254,7 @@
 
 ```
 
-> You can get the full code [here](../guide/example/example-formats-14.kt).
+> You can get the full code [here](../guide/example/example-formats-15.kt).
 
 In the output we see how not-null`!!` and `NULL` marks are used.
 
@@ -1037,7 +1280,8 @@
 import java.io.*
 -->
 
-```kotlin            
+```kotlin     
+@ExperimentalSerializationApi
 class DataOutputEncoder(val output: DataOutput) : AbstractEncoder() {
     override val serializersModule: SerializersModule = EmptySerializersModule()
     override fun encodeBoolean(value: Boolean) = output.writeByte(if (value) 1 else 0)
@@ -1063,17 +1307,20 @@
 
 <!--- INCLUDE
 
+@ExperimentalSerializationApi
 fun <T> encodeTo(output: DataOutput, serializer: SerializationStrategy<T>, value: T) {
     val encoder = DataOutputEncoder(output)
     encoder.encodeSerializableValue(serializer, value)
 }
 
+@ExperimentalSerializationApi
 inline fun <reified T> encodeTo(output: DataOutput, value: T) = encodeTo(output, serializer(), value)
 -->
 
 The decoder implementation mirrors encoder's implementation overriding all the primitive `decodeXxx` functions.
 
 ```kotlin 
+@ExperimentalSerializationApi
 class DataInputDecoder(val input: DataInput, var elementsCount: Int = 0) : AbstractDecoder() {
     private var elementIndex = 0
     override val serializersModule: SerializersModule = EmptySerializersModule()
@@ -1107,11 +1354,13 @@
 
 <!--- INCLUDE
 
+@ExperimentalSerializationApi
 fun <T> decodeFrom(input: DataInput, deserializer: DeserializationStrategy<T>): T {
     val decoder = DataInputDecoder(input)
     return decoder.decodeSerializableValue(deserializer)
 }
 
+@ExperimentalSerializationApi
 inline fun <reified T> decodeFrom(input: DataInput): T = decodeFrom(input, serializer())
 
 fun ByteArray.toAsciiHexString() = joinToString("") {
@@ -1127,6 +1376,7 @@
 @Serializable
 data class Project(val name: String, val language: String)
 
+@OptIn(ExperimentalSerializationApi::class)
 fun main() {
     val data = Project("kotlinx.serialization", "Kotlin")
     val output = ByteArrayOutputStream()
@@ -1139,7 +1389,7 @@
 }
 ```
               
-> You can get the full code [here](../guide/example/example-formats-15.kt).
+> You can get the full code [here](../guide/example/example-formats-16.kt).
 
 As we can see, the result is a dense binary format that only contains the data that is being serialized. 
 It can be easily tweaked for any kind of domain-specific compact encoding.
@@ -1190,6 +1440,7 @@
 a size of up to 254 bytes.  
 
 <!--- INCLUDE
+@ExperimentalSerializationApi
 class DataOutputEncoder(val output: DataOutput) : AbstractEncoder() {
     override val serializersModule: SerializersModule = EmptySerializersModule()
     override fun encodeBoolean(value: Boolean) = output.writeByte(if (value) 1 else 0)
@@ -1238,13 +1489,16 @@
 <!--- INCLUDE
 }
 
+@ExperimentalSerializationApi
 fun <T> encodeTo(output: DataOutput, serializer: SerializationStrategy<T>, value: T) {
     val encoder = DataOutputEncoder(output)
     encoder.encodeSerializableValue(serializer, value)
 }
 
+@ExperimentalSerializationApi
 inline fun <reified T> encodeTo(output: DataOutput, value: T) = encodeTo(output, serializer(), value)
 
+@ExperimentalSerializationApi
 class DataInputDecoder(val input: DataInput, var elementsCount: Int = 0) : AbstractDecoder() {
     private var elementIndex = 0
     override val serializersModule: SerializersModule = EmptySerializersModule()
@@ -1302,11 +1556,13 @@
 <!--- INCLUDE
 }
 
+@ExperimentalSerializationApi
 fun <T> decodeFrom(input: DataInput, deserializer: DeserializationStrategy<T>): T {
     val decoder = DataInputDecoder(input)
     return decoder.decodeSerializableValue(deserializer)
 }
 
+@ExperimentalSerializationApi
 inline fun <reified T> decodeFrom(input: DataInput): T = decodeFrom(input, serializer())
 
 fun ByteArray.toAsciiHexString() = joinToString("") {
@@ -1321,6 +1577,7 @@
 @Serializable
 data class Project(val name: String, val attachment: ByteArray)
 
+@OptIn(ExperimentalSerializationApi::class)
 fun main() {
     val data = Project("kotlinx.serialization", byteArrayOf(0x0A, 0x0B, 0x0C, 0x0D))
     val output = ByteArrayOutputStream()
@@ -1333,7 +1590,7 @@
 }
 ```
               
-> You can get the full code [here](../guide/example/example-formats-16.kt).
+> You can get the full code [here](../guide/example/example-formats-17.kt).
 
 As we can see, our custom byte array format is being used, with the compact encoding of its size in one byte. 
 
diff --git a/docs/json.md b/docs/json.md
index c637eb1..234d7c9 100644
--- a/docs/json.md
+++ b/docs/json.md
@@ -14,15 +14,16 @@
   * [Lenient parsing](#lenient-parsing)
   * [Ignoring unknown keys](#ignoring-unknown-keys)
   * [Alternative Json names](#alternative-json-names)
-  * [Coercing input values](#coercing-input-values)
   * [Encoding defaults](#encoding-defaults)
   * [Explicit nulls](#explicit-nulls)
+  * [Coercing input values](#coercing-input-values)
   * [Allowing structured map keys](#allowing-structured-map-keys)
   * [Allowing special floating-point values](#allowing-special-floating-point-values)
   * [Class discriminator for polymorphism](#class-discriminator-for-polymorphism)
   * [Class discriminator output mode](#class-discriminator-output-mode)
   * [Decoding enums in a case-insensitive manner](#decoding-enums-in-a-case-insensitive-manner)
   * [Global naming strategy](#global-naming-strategy)
+  * [Base64](#base64)
 * [Json elements](#json-elements)
   * [Parsing to Json element](#parsing-to-json-element)
   * [Types of Json elements](#types-of-json-elements)
@@ -36,6 +37,7 @@
   * [Array unwrapping](#array-unwrapping)
   * [Manipulating default values](#manipulating-default-values)
   * [Content-based polymorphic deserialization](#content-based-polymorphic-deserialization)
+  * [Extending the behavior of the plugin generated serializer](#extending-the-behavior-of-the-plugin-generated-serializer)
   * [Under the hood (experimental)](#under-the-hood-experimental)
   * [Maintaining custom JSON attributes](#maintaining-custom-json-attributes)
 
@@ -120,13 +122,15 @@
 
 > You can get the full code [here](../guide/example/example-json-02.kt).
 
-You get the object, even though all keys of the source JSON, string, and enum values are unquoted, while an
-integer is quoted:
+You get the object, even though all keys of the source JSON, string and enum values are unquoted:
 
 ```text
 Project(name=kotlinx.serialization, status=SUPPORTED, votes=9000)
 ```
 
+> Note that parsing of quoted numbers or booleans such as `votes: "9000"` to `val votes: Int` is generally allowed by kotlinx.serialization
+> regardless of the `isLenient` flag, since such JSON is syntactically valid.
+
 <!--- TEST -->
 
 ### Ignoring unknown keys
@@ -168,6 +172,7 @@
 To support multiple JSON names for the one Kotlin property, there is the [JsonNames] annotation:
 
 ```kotlin
+@OptIn(ExperimentalSerializationApi::class) // JsonNames is an experimental annotation for now
 @Serializable
 data class Project(@JsonNames("title") val name: String)
 
@@ -194,51 +199,6 @@
 
 <!--- TEST -->
 
-### Coercing input values
-
-JSON formats that from third parties can evolve, sometimes changing the field types.
-This can lead to exceptions during decoding when the actual values do not match the expected values.
-The default [Json] implementation is strict with respect to input types as was demonstrated in
-the [Type safety is enforced](basic-serialization.md#type-safety-is-enforced) section. You can relax this restriction
-using the [coerceInputValues][JsonBuilder.coerceInputValues] property.
-
-This property only affects decoding. It treats a limited subset of invalid input values as if the
-corresponding property was missing and uses the default value of the corresponding property instead.
-The current list of supported invalid values is:
-
-* `null` inputs for non-nullable types
-* unknown values for enums
-
-> This list may be expanded in the future, so that [Json] instance configured with this property becomes even more
-> permissive to invalid value in the input, replacing them with defaults.
-
-See the example from the [Type safety is enforced](basic-serialization.md#type-safety-is-enforced) section:
-
-```kotlin
-val format = Json { coerceInputValues = true }
-
-@Serializable
-data class Project(val name: String, val language: String = "Kotlin")
-
-fun main() {
-    val data = format.decodeFromString<Project>("""
-        {"name":"kotlinx.serialization","language":null}
-    """)
-    println(data)
-}
-```
-
-> You can get the full code [here](../guide/example/example-json-05.kt).
-
-The invalid `null` value for the `language` property was coerced into the default value:
-
-```text
-Project(name=kotlinx.serialization, language=Kotlin)
-```
-
-<!--- TEST -->
-
-
 ### Encoding defaults
 
 Default values of properties are not encoded by default because they will be assigned to missing fields during decoding anyway.
@@ -262,7 +222,7 @@
 }
 ```
 
-> You can get the full code [here](../guide/example/example-json-06.kt).
+> You can get the full code [here](../guide/example/example-json-05.kt).
 
 It produces the following output which encodes all the property values including the default ones:
 
@@ -301,7 +261,7 @@
 }
 ```
 
-> You can get the full code [here](../guide/example/example-json-07.kt).
+> You can get the full code [here](../guide/example/example-json-06.kt).
 
 As you can see, `version`, `website` and `description` fields are not present in output JSON on the first line.
 After decoding, the missing nullable property `website` without a default values has received a `null` value,
@@ -312,10 +272,94 @@
 Project(name=kotlinx.serialization, language=Kotlin, version=1.2.2, website=null, description=null)
 ```
 
+> Pay attention to the fact that `version` was `null` before encoding and became `1.2.2` after decoding. 
+> Encoding/decoding of properties like this — nullable with a non-null default — becomes asymmetrical if `explicitNulls` is set to `false`.
+
+It is possible to make the decoder treat some invalid input data as a missing field to enhance the functionality of this flag.
+See [coerceInputValues](#coercing-input-values) below for details.
+
 `explicitNulls` is `true` by default as it is the default behavior across different versions of the library.
 
 <!--- TEST -->
 
+### Coercing input values
+
+JSON formats that from third parties can evolve, sometimes changing the field types.
+This can lead to exceptions during decoding when the actual values do not match the expected values.
+The default [Json] implementation is strict with respect to input types as was demonstrated in
+the [Type safety is enforced](basic-serialization.md#type-safety-is-enforced) section. You can relax this restriction
+using the [coerceInputValues][JsonBuilder.coerceInputValues] property.
+
+This property only affects decoding. It treats a limited subset of invalid input values as if the
+corresponding property was missing.
+The current list of supported invalid values is:
+
+* `null` inputs for non-nullable types
+* unknown values for enums
+
+If value is missing, it is replaced either with a default property value if it exists,
+or with a `null` if [explicitNulls](#explicit-nulls) flag is set to `false` and a property is nullable (for enums).
+
+> This list may be expanded in the future, so that [Json] instance configured with this property becomes even more
+> permissive to invalid value in the input, replacing them with defaults or nulls.
+
+See the example from the [Type safety is enforced](basic-serialization.md#type-safety-is-enforced) section:
+
+```kotlin
+val format = Json { coerceInputValues = true }
+
+@Serializable
+data class Project(val name: String, val language: String = "Kotlin")
+
+fun main() {
+    val data = format.decodeFromString<Project>("""
+        {"name":"kotlinx.serialization","language":null}
+    """)
+    println(data)
+}
+```
+
+> You can get the full code [here](../guide/example/example-json-07.kt).
+
+The invalid `null` value for the `language` property was coerced into the default value:
+
+```text
+Project(name=kotlinx.serialization, language=Kotlin)
+```
+
+<!--- TEST -->
+
+Example of using this flag together with [explicitNulls](#explicit-nulls) to coerce invalid enum values:
+
+```kotlin
+enum class Color { BLACK, WHITE }
+
+@Serializable
+data class Brush(val foreground: Color = Color.BLACK, val background: Color?)
+
+val json = Json { 
+  coerceInputValues = true
+  explicitNulls = false
+}
+
+fun main() {
+    val brush = json.decodeFromString<Brush>("""{"foreground":"pink", "background":"purple"}""")
+  println(brush)
+}
+```
+
+> You can get the full code [here](../guide/example/example-json-08.kt).
+
+Despite that we do not have `Color.pink` and `Color.purple` colors, `decodeFromString` function returns successfully:
+
+```text
+Brush(foreground=BLACK, background=null)
+```
+
+`foreground` property received its default value, and `background` property received `null` because of `explicitNulls = false` setting.
+
+<!--- TEST -->
+
 ### Allowing structured map keys
 
 JSON format does not natively support the concept of a map with structured keys. Keys in JSON objects
@@ -340,7 +384,7 @@
 }
 ```
 
-> You can get the full code [here](../guide/example/example-json-08.kt).
+> You can get the full code [here](../guide/example/example-json-09.kt).
 
 The map with structured keys gets represented as JSON array with the following items: `[key1, value1, key2, value2,...]`.
 
@@ -371,7 +415,7 @@
 }
 ```
 
-> You can get the full code [here](../guide/example/example-json-09.kt).
+> You can get the full code [here](../guide/example/example-json-10.kt).
 
 This example produces the following non-stardard JSON output, yet it is a widely used encoding for
 special values in JVM world:
@@ -405,7 +449,7 @@
 }
 ```
 
-> You can get the full code [here](../guide/example/example-json-10.kt).
+> You can get the full code [here](../guide/example/example-json-11.kt).
 
 In combination with an explicitly specified [SerialName] of the class it provides full
 control over the resulting JSON object:
@@ -419,6 +463,7 @@
 It is also possible to specify different class discriminators for different hierarchies. Instead of Json instance property, use [JsonClassDiscriminator] annotation directly on base serializable class:
 
 ```kotlin
+@OptIn(ExperimentalSerializationApi::class) // JsonClassDiscriminator is an experimental annotation for now
 @Serializable
 @JsonClassDiscriminator("message_type")
 sealed class Base
@@ -461,7 +506,7 @@
 }
 ```
 
-> You can get the full code [here](../guide/example/example-json-11.kt).
+> You can get the full code [here](../guide/example/example-json-12.kt).
 
 As you can see, discriminator from the `Base` class is used:
 
@@ -481,6 +526,7 @@
 For example, [ClassDiscriminatorMode.NONE] does not add class discriminator at all, in case the receiving party is not interested in Kotlin types:
 
 ```kotlin
+@OptIn(ExperimentalSerializationApi::class) // classDiscriminatorMode is an experimental setting for now
 val format = Json { classDiscriminatorMode = ClassDiscriminatorMode.NONE }
 
 @Serializable
@@ -497,7 +543,7 @@
 }
 ```
 
-> You can get the full code [here](../guide/example/example-json-12.kt).
+> You can get the full code [here](../guide/example/example-json-13.kt).
 
 Note that it would be impossible to deserialize this output back with kotlinx.serialization.
 
@@ -519,8 +565,10 @@
 In this case, it is possible to decode enum values in a case-insensitive manner using [JsonBuilder.decodeEnumsCaseInsensitive] property:
 
 ```kotlin
+@OptIn(ExperimentalSerializationApi::class) // decodeEnumsCaseInsensitive is an experimental setting for now
 val format = Json { decodeEnumsCaseInsensitive = true }
 
+@OptIn(ExperimentalSerializationApi::class) // JsonNames is an experimental annotation for now
 enum class Cases { VALUE_A, @JsonNames("Alternative") VALUE_B }
 
 @Serializable
@@ -531,7 +579,7 @@
 }
 ```
 
-> You can get the full code [here](../guide/example/example-json-13.kt).
+> You can get the full code [here](../guide/example/example-json-14.kt).
 
 It affects serial names as well as alternative names specified with [JsonNames] annotation, so both values are successfully decoded:
 
@@ -555,6 +603,7 @@
 @Serializable
 data class Project(val projectName: String, val projectOwner: String)
 
+@OptIn(ExperimentalSerializationApi::class) // namingStrategy is an experimental setting for now
 val format = Json { namingStrategy = JsonNamingStrategy.SnakeCase }
 
 fun main() {
@@ -563,7 +612,7 @@
 }
 ```
 
-> You can get the full code [here](../guide/example/example-json-14.kt).
+> You can get the full code [here](../guide/example/example-json-15.kt).
 
 As you can see, both serialization and deserialization work as if all serial names are transformed from camel case to snake case:
 
@@ -591,6 +640,94 @@
 
 <!--- TEST -->
 
+### Base64
+
+To encode and decode Base64 formats, we will need to manually write a serializer. Here, we will use a default
+implementation of Kotlin's Base64 encoder. Note that some serializers use different RFCs for Base64 encoding by default.
+For example, Jackson uses a variant of [Base64 Mime](https://datatracker.ietf.org/doc/html/rfc2045). The same result in
+kotlinx.serialization can be achieved with Base64.Mime encoder.
+[Kotlin's documentation for Base64](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io.encoding/-base64/) lists
+other available encoders.
+
+```kotlin
+import kotlinx.serialization.encoding.Encoder
+import kotlinx.serialization.encoding.Decoder
+import kotlinx.serialization.descriptors.*
+import kotlin.io.encoding.*
+
+@OptIn(ExperimentalEncodingApi::class)
+object ByteArrayAsBase64Serializer : KSerializer<ByteArray> {
+    private val base64 = Base64.Default
+
+    override val descriptor: SerialDescriptor
+        get() = PrimitiveSerialDescriptor(
+            "ByteArrayAsBase64Serializer",
+            PrimitiveKind.STRING
+        )
+
+    override fun serialize(encoder: Encoder, value: ByteArray) {
+        val base64Encoded = base64.encode(value)
+        encoder.encodeString(base64Encoded)
+    }
+
+    override fun deserialize(decoder: Decoder): ByteArray {
+        val base64Decoded = decoder.decodeString()
+        return base64.decode(base64Decoded)
+    }
+}
+```
+
+For more details on how to create your own custom serializer, you can
+see [custom serializers](serializers.md#custom-serializers).
+
+Then we can use it like this:
+
+```kotlin
+@Serializable
+data class Value(
+    @Serializable(with = ByteArrayAsBase64Serializer::class)
+    val base64Input: ByteArray
+) {
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
+        other as Value
+        return base64Input.contentEquals(other.base64Input)
+    }
+
+    override fun hashCode(): Int {
+        return base64Input.contentHashCode()
+    }
+}
+
+fun main() {
+    val string = "foo string"
+    val value = Value(string.toByteArray())
+    val encoded = Json.encodeToString(value)
+    println(encoded)
+    val decoded = Json.decodeFromString<Value>(encoded)
+    println(decoded.base64Input.decodeToString())
+}
+```
+
+> You can get the full code [here](../guide/example/example-json-16.kt)
+
+```text
+{"base64Input":"Zm9vIHN0cmluZw=="}
+foo string
+```
+
+Notice the serializer we wrote is not dependent on `Json` format, therefore, it can be used in any format.
+
+For projects that use this serializer in many places, to avoid specifying the serializer every time, it is possible
+to [specify a serializer globally using typealias](serializers.md#specifying-serializer-globally-using-typealias).
+For example:
+````kotlin
+typealias Base64ByteArray = @Serializable(ByteArrayAsBase64Serializer::class) ByteArray
+````
+
+<!--- TEST -->
+
 ## Json elements
 
 Aside from direct conversions between strings and JSON objects, Kotlin serialization offers APIs that allow
@@ -615,7 +752,7 @@
 }
 ```
 
-> You can get the full code [here](../guide/example/example-json-15.kt).
+> You can get the full code [here](../guide/example/example-json-17.kt).
 
 A `JsonElement` prints itself as a valid JSON:
 
@@ -658,7 +795,7 @@
 }
 ```
 
-> You can get the full code [here](../guide/example/example-json-16.kt).
+> You can get the full code [here](../guide/example/example-json-18.kt).
 
 The above example sums `votes` in all objects in the `forks` array, ignoring the objects that have no `votes`:
 
@@ -698,7 +835,7 @@
 }
 ```
 
-> You can get the full code [here](../guide/example/example-json-17.kt).
+> You can get the full code [here](../guide/example/example-json-19.kt).
 
 As a result, you get a proper JSON string:
 
@@ -727,7 +864,7 @@
 }
 ```
 
-> You can get the full code [here](../guide/example/example-json-18.kt).
+> You can get the full code [here](../guide/example/example-json-20.kt).
 
 The result is exactly what you would expect:
 
@@ -773,7 +910,7 @@
 }
 ```
 
-> You can get the full code [here](../guide/example/example-json-19.kt).
+> You can get the full code [here](../guide/example/example-json-21.kt).
 
 Even though `pi` was defined as a number with 30 decimal places, the resulting JSON does not reflect this. 
 The [Double] value is truncated to 15 decimal places, and the String is wrapped in quotes - which is not a JSON number.
@@ -798,6 +935,7 @@
     val pi = BigDecimal("3.141592653589793238462643383279")
 
     // use JsonUnquotedLiteral to encode raw JSON content
+    @OptIn(ExperimentalSerializationApi::class)
     val piJsonLiteral = JsonUnquotedLiteral(pi.toString())
 
     val piJsonDouble = JsonPrimitive(pi.toDouble())
@@ -813,7 +951,7 @@
 }
 ```
 
-> You can get the full code [here](../guide/example/example-json-20.kt).
+> You can get the full code [here](../guide/example/example-json-22.kt).
 
 `pi_literal` now accurately matches the value defined.
 
@@ -853,7 +991,7 @@
 }
 ```
 
-> You can get the full code [here](../guide/example/example-json-21.kt).
+> You can get the full code [here](../guide/example/example-json-23.kt).
 
 The exact value of `pi` is decoded, with all 30 decimal places of precision that were in the source JSON.
 
@@ -869,13 +1007,14 @@
 Use [JsonNull] or [JsonPrimitive] instead.
 
 ```kotlin
+@OptIn(ExperimentalSerializationApi::class)
 fun main() {
     // caution: creating null with JsonUnquotedLiteral will cause an exception! 
     JsonUnquotedLiteral("null")
 }
 ```
 
-> You can get the full code [here](../guide/example/example-json-22.kt).
+> You can get the full code [here](../guide/example/example-json-24.kt).
 
 ```text
 Exception in thread "main" kotlinx.serialization.json.internal.JsonEncodingException: Creating a literal unquoted value of 'null' is forbidden. If you want to create JSON null literal, use JsonNull object, otherwise, use JsonPrimitive
@@ -951,7 +1090,7 @@
 }
 ```
 
-> You can get the full code [here](../guide/example/example-json-23.kt).
+> You can get the full code [here](../guide/example/example-json-25.kt).
 
 The output shows that both cases are correctly deserialized into a Kotlin [List].
 
@@ -1003,7 +1142,7 @@
 }
 ```
 
-> You can get the full code [here](../guide/example/example-json-24.kt).
+> You can get the full code [here](../guide/example/example-json-26.kt).
 
 You end up with a single JSON object, not an array with one element:
 
@@ -1048,7 +1187,7 @@
 }
 ```
 
-> You can get the full code [here](../guide/example/example-json-25.kt).
+> You can get the full code [here](../guide/example/example-json-27.kt).
 
 See the effect of the custom serializer:
 
@@ -1121,7 +1260,7 @@
 }
 ```
 
-> You can get the full code [here](../guide/example/example-json-26.kt).
+> You can get the full code [here](../guide/example/example-json-28.kt).
 
 No class discriminator is added in the JSON output:
 
@@ -1132,6 +1271,56 @@
 
 <!--- TEST -->
 
+### Extending the behavior of the plugin generated serializer
+In some cases, it may be necessary to add additional serialization logic on top of the plugin generated logic.
+For example, to add a preliminary modification of JSON elements or to add processing of unknown values of enums.
+
+In this case, you can mark the serializable class with the [`@KeepGeneratedSerializer`][KeepGeneratedSerializer] annotation and get the generated serializer using the `generatedSerializer()` function.
+
+> This annotation is currently experimental. Kotlin 2.0.20 or higher is required for this feature to work.
+
+Here is an example of the simultaneous use of [JsonTransformingSerializer] and polymorphism.
+In this example, we use `transformDeserialize` function to rename `basic-name` key into `name` so it matches the `abstract val name` property from the `Project` supertype.
+```kotlin
+@Serializable
+sealed class Project {
+    abstract val name: String
+}
+
+@OptIn(ExperimentalSerializationApi::class)
+@KeepGeneratedSerializer
+@Serializable(with = BasicProjectSerializer::class)
+@SerialName("basic")
+data class BasicProject(override val name: String): Project()
+
+object BasicProjectSerializer : JsonTransformingSerializer<BasicProject>(BasicProject.generatedSerializer()) {
+    override fun transformDeserialize(element: JsonElement): JsonElement {
+        val jsonObject = element.jsonObject
+        return if ("basic-name" in jsonObject) {
+            val nameElement = jsonObject["basic-name"] ?: throw IllegalStateException()
+            JsonObject(mapOf("name" to nameElement))
+        } else {
+            jsonObject
+        }
+    }
+}
+
+
+fun main() {
+    val project = Json.decodeFromString<Project>("""{"type":"basic","basic-name":"example"}""")
+    println(project)
+}
+```
+
+> You can get the full code [here](../guide/example/example-json-29.kt).
+
+`BasicProject` will be printed to the output:
+
+```text
+BasicProject(name=example)
+```
+<!--- TEST -->
+
 ### Under the hood (experimental)
 
 Although abstract serializers mentioned above can cover most of the cases, it is possible to implement similar machinery
@@ -1167,7 +1356,7 @@
 }
 
 class ResponseSerializer<T>(private val dataSerializer: KSerializer<T>) : KSerializer<Response<T>> {
-    override val descriptor: SerialDescriptor = buildSerialDescriptor("Response", PolymorphicKind.SEALED) {
+    override val descriptor: SerialDescriptor = buildClassSerialDescriptor("Response") {
         element("Ok", dataSerializer.descriptor)
         element("Error", buildClassSerialDescriptor("Error") {
           element<String>("message")
@@ -1217,7 +1406,7 @@
 }
 ```
 
-> You can get the full code [here](../guide/example/example-json-27.kt).
+> You can get the full code [here](../guide/example/example-json-30.kt).
 
 This gives you fine-grained control on the representation of the `Response` class in the JSON output:
 
@@ -1282,7 +1471,7 @@
 }
 ```
 
-> You can get the full code [here](../guide/example/example-json-28.kt).
+> You can get the full code [here](../guide/example/example-json-31.kt).
 
 ```text
 UnknownProject(name=example, details={"type":"unknown","maintainer":"Unknown","license":"Apache 2.0"})
@@ -1312,6 +1501,7 @@
 [InheritableSerialInfo]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-inheritable-serial-info/index.html
 [KSerializer]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-k-serializer/index.html
 [Serializable]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serializable/index.html
+[KeepGeneratedSerializer]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-keep-generated-serializer/index.html
 
 <!--- INDEX kotlinx-serialization-core/kotlinx.serialization.encoding -->
 
@@ -1329,9 +1519,9 @@
 [JsonBuilder.ignoreUnknownKeys]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/ignore-unknown-keys.html
 [JsonNames]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-names/index.html
 [JsonBuilder.useAlternativeNames]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/use-alternative-names.html
-[JsonBuilder.coerceInputValues]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/coerce-input-values.html
 [JsonBuilder.encodeDefaults]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/encode-defaults.html
 [JsonBuilder.explicitNulls]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/explicit-nulls.html
+[JsonBuilder.coerceInputValues]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/coerce-input-values.html
 [JsonBuilder.allowStructuredMapKeys]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/allow-structured-map-keys.html
 [JsonBuilder.allowSpecialFloatingPointValues]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/allow-special-floating-point-values.html
 [JsonBuilder.classDiscriminator]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/class-discriminator.html
diff --git a/docs/polymorphism.md b/docs/polymorphism.md
index 2661e04..67f0560 100644
--- a/docs/polymorphism.md
+++ b/docs/polymorphism.md
@@ -293,7 +293,7 @@
 
 > You can get the full code [here](../guide/example/example-poly-08.kt).
 
-An object serializes as an empty class, also using its fully-qualified class name as type by default:
+An object serializes as an empty class, also using its fully qualified class name as type by default:
 
 ```text 
 [{"type":"example.examplePoly08.EmptyResponse"},{"type":"example.examplePoly08.TextResponse","text":"OK"}]
@@ -713,7 +713,7 @@
  
 Kotlin Serialization does not have a builtin strategy to represent the actually provided argument type for the
 type parameter `T` when serializing a property of the polymorphic type `OkResponse<T>`. We have to provide this 
-strategy explicitly when defining the serializers module for the `Response`. In the below example we
+strategy explicitly when defining the serializers module for `Response`. In the below example we
 use `OkResponse.serializer(...)` to retrieve 
 the [Plugin-generated generic serializer](serializers.md#plugin-generated-generic-serializer) of
 the `OkResponse` class and instantiate it with the [PolymorphicSerializer] instance with 
diff --git a/docs/serialization-guide.md b/docs/serialization-guide.md
index 50cb130..01ada5f 100644
--- a/docs/serialization-guide.md
+++ b/docs/serialization-guide.md
@@ -66,16 +66,17 @@
   * <a name='primitive-serializer'></a>[Primitive serializer](serializers.md#primitive-serializer)
   * <a name='delegating-serializers'></a>[Delegating serializers](serializers.md#delegating-serializers)
   * <a name='composite-serializer-via-surrogate'></a>[Composite serializer via surrogate](serializers.md#composite-serializer-via-surrogate)
-  * <a name='hand-written-composite-serializer'></a>[Hand-written composite serializer](serializers.md#hand-written-composite-serializer)
+  * <a name='handwritten-composite-serializer'></a>[Handwritten composite serializer](serializers.md#handwritten-composite-serializer)
   * <a name='sequential-decoding-protocol-experimental'></a>[Sequential decoding protocol (experimental)](serializers.md#sequential-decoding-protocol-experimental)
   * <a name='serializing-3rd-party-classes'></a>[Serializing 3rd party classes](serializers.md#serializing-3rd-party-classes)
   * <a name='passing-a-serializer-manually'></a>[Passing a serializer manually](serializers.md#passing-a-serializer-manually)
-  * <a name='specifying-serializer-on-a-property'></a>[Specifying serializer on a property](serializers.md#specifying-serializer-on-a-property)
-  * <a name='specifying-serializer-for-a-particular-type'></a>[Specifying serializer for a particular type](serializers.md#specifying-serializer-for-a-particular-type)
+  * <a name='specifying-a-serializer-on-a-property'></a>[Specifying a serializer on a property](serializers.md#specifying-a-serializer-on-a-property)
+  * <a name='specifying-a-serializer-for-a-particular-type'></a>[Specifying a serializer for a particular type](serializers.md#specifying-a-serializer-for-a-particular-type)
   * <a name='specifying-serializers-for-a-file'></a>[Specifying serializers for a file](serializers.md#specifying-serializers-for-a-file)
-  * <a name='specifying-serializer-globally-using-typealias'></a>[Specifying serializer globally using typealias](serializers.md#specifying-serializer-globally-using-typealias)
+  * <a name='specifying-a-serializer-globally-using-a-typealias'></a>[Specifying a serializer globally using a typealias](serializers.md#specifying-a-serializer-globally-using-a-typealias)
   * <a name='custom-serializers-for-a-generic-type'></a>[Custom serializers for a generic type](serializers.md#custom-serializers-for-a-generic-type)
   * <a name='format-specific-serializers'></a>[Format-specific serializers](serializers.md#format-specific-serializers)
+* <a name='simultaneous-use-of-plugin-generated-and-custom-serializers'></a>[Simultaneous use of plugin-generated and custom serializers](serializers.md#simultaneous-use-of-plugin-generated-and-custom-serializers)
 * <a name='contextual-serialization'></a>[Contextual serialization](serializers.md#contextual-serialization)
   * <a name='serializers-module'></a>[Serializers module](serializers.md#serializers-module)
   * <a name='contextual-serialization-and-generic-classes'></a>[Contextual serialization and generic classes](serializers.md#contextual-serialization-and-generic-classes)
@@ -114,15 +115,16 @@
   * <a name='lenient-parsing'></a>[Lenient parsing](json.md#lenient-parsing)
   * <a name='ignoring-unknown-keys'></a>[Ignoring unknown keys](json.md#ignoring-unknown-keys)
   * <a name='alternative-json-names'></a>[Alternative Json names](json.md#alternative-json-names)
-  * <a name='coercing-input-values'></a>[Coercing input values](json.md#coercing-input-values)
   * <a name='encoding-defaults'></a>[Encoding defaults](json.md#encoding-defaults)
   * <a name='explicit-nulls'></a>[Explicit nulls](json.md#explicit-nulls)
+  * <a name='coercing-input-values'></a>[Coercing input values](json.md#coercing-input-values)
   * <a name='allowing-structured-map-keys'></a>[Allowing structured map keys](json.md#allowing-structured-map-keys)
   * <a name='allowing-special-floating-point-values'></a>[Allowing special floating-point values](json.md#allowing-special-floating-point-values)
   * <a name='class-discriminator-for-polymorphism'></a>[Class discriminator for polymorphism](json.md#class-discriminator-for-polymorphism)
   * <a name='class-discriminator-output-mode'></a>[Class discriminator output mode](json.md#class-discriminator-output-mode)
   * <a name='decoding-enums-in-a-case-insensitive-manner'></a>[Decoding enums in a case-insensitive manner](json.md#decoding-enums-in-a-case-insensitive-manner)
   * <a name='global-naming-strategy'></a>[Global naming strategy](json.md#global-naming-strategy)
+  * <a name='base64'></a>[Base64](json.md#base64)
 * <a name='json-elements'></a>[Json elements](json.md#json-elements)
   * <a name='parsing-to-json-element'></a>[Parsing to Json element](json.md#parsing-to-json-element)
   * <a name='types-of-json-elements'></a>[Types of Json elements](json.md#types-of-json-elements)
@@ -136,6 +138,7 @@
   * <a name='array-unwrapping'></a>[Array unwrapping](json.md#array-unwrapping)
   * <a name='manipulating-default-values'></a>[Manipulating default values](json.md#manipulating-default-values)
   * <a name='content-based-polymorphic-deserialization'></a>[Content-based polymorphic deserialization](json.md#content-based-polymorphic-deserialization)
+  * <a name='extending-the-behavior-of-the-plugin-generated-serializer'></a>[Extending the behavior of the plugin generated serializer](json.md#extending-the-behavior-of-the-plugin-generated-serializer)
   * <a name='under-the-hood-experimental'></a>[Under the hood (experimental)](json.md#under-the-hood-experimental)
   * <a name='maintaining-custom-json-attributes'></a>[Maintaining custom JSON attributes](json.md#maintaining-custom-json-attributes)
 <!--- END -->
@@ -146,11 +149,18 @@
 * <a name='cbor-experimental'></a>[CBOR (experimental)](formats.md#cbor-experimental)
   * <a name='ignoring-unknown-keys'></a>[Ignoring unknown keys](formats.md#ignoring-unknown-keys)
   * <a name='byte-arrays-and-cbor-data-types'></a>[Byte arrays and CBOR data types](formats.md#byte-arrays-and-cbor-data-types)
+  * <a name='definite-vs-indefinite-length-encoding'></a>[Definite vs. Indefinite Length Encoding](formats.md#definite-vs-indefinite-length-encoding)
+  * <a name='tags-and-labels'></a>[Tags and Labels](formats.md#tags-and-labels)
+  * <a name='arrays'></a>[Arrays](formats.md#arrays)
+  * <a name='custom-cbor-specific-serializers'></a>[Custom CBOR-specific Serializers](formats.md#custom-cbor-specific-serializers)
 * <a name='protobuf-experimental'></a>[ProtoBuf (experimental)](formats.md#protobuf-experimental)
   * <a name='field-numbers'></a>[Field numbers](formats.md#field-numbers)
   * <a name='integer-types'></a>[Integer types](formats.md#integer-types)
   * <a name='lists-as-repeated-fields'></a>[Lists as repeated fields](formats.md#lists-as-repeated-fields)
   * <a name='packed-fields'></a>[Packed fields](formats.md#packed-fields)
+  * <a name='oneof-field-experimental'></a>[Oneof field (experimental)](formats.md#oneof-field-experimental)
+    * <a name='usage'></a>[Usage](formats.md#usage)
+    * <a name='alternative'></a>[Alternative](formats.md#alternative)
   * <a name='protobuf-schema-generator-experimental'></a>[ProtoBuf schema generator (experimental)](formats.md#protobuf-schema-generator-experimental)
 * <a name='properties-experimental'></a>[Properties (experimental)](formats.md#properties-experimental)
 * <a name='custom-formats-experimental'></a>[Custom formats (experimental)](formats.md#custom-formats-experimental)
diff --git a/docs/serializers.md b/docs/serializers.md
index e6bf78e..19542cc 100644
--- a/docs/serializers.md
+++ b/docs/serializers.md
@@ -19,16 +19,17 @@
   * [Primitive serializer](#primitive-serializer)
   * [Delegating serializers](#delegating-serializers)
   * [Composite serializer via surrogate](#composite-serializer-via-surrogate)
-  * [Hand-written composite serializer](#hand-written-composite-serializer)
+  * [Handwritten composite serializer](#handwritten-composite-serializer)
   * [Sequential decoding protocol (experimental)](#sequential-decoding-protocol-experimental)
   * [Serializing 3rd party classes](#serializing-3rd-party-classes)
   * [Passing a serializer manually](#passing-a-serializer-manually)
-  * [Specifying serializer on a property](#specifying-serializer-on-a-property)
-  * [Specifying serializer for a particular type](#specifying-serializer-for-a-particular-type)
+  * [Specifying a serializer on a property](#specifying-a-serializer-on-a-property)
+  * [Specifying a serializer for a particular type](#specifying-a-serializer-for-a-particular-type)
   * [Specifying serializers for a file](#specifying-serializers-for-a-file)
-  * [Specifying serializer globally using typealias](#specifying-serializer-globally-using-typealias)
+  * [Specifying a serializer globally using a typealias](#specifying-a-serializer-globally-using-a-typealias)
   * [Custom serializers for a generic type](#custom-serializers-for-a-generic-type)
   * [Format-specific serializers](#format-specific-serializers)
+* [Simultaneous use of plugin-generated and custom serializers](#simultaneous-use-of-plugin-generated-and-custom-serializers)
 * [Contextual serialization](#contextual-serialization)
   * [Serializers module](#serializers-module)
   * [Contextual serialization and generic classes](#contextual-serialization-and-generic-classes)
@@ -164,9 +165,11 @@
 
 > You can get the full code [here](../guide/example/example-serializer-04.kt).   
 
-<!--- TEST 
+```text
 PrimitiveDescriptor(kotlin.Int)
---> 
+```
+
+<!--- TEST -->
 
 ### Constructing collection serializers
 
@@ -190,9 +193,11 @@
 
 > You can get the full code [here](../guide/example/example-serializer-05.kt).  
 
-<!--- TEST 
+```text
 kotlin.collections.ArrayList(PrimitiveDescriptor(kotlin.String))
---> 
+```
+
+<!--- TEST -->
 
 ### Using top-level serializer function
 
@@ -216,14 +221,17 @@
 
 > You can get the full code [here](../guide/example/example-serializer-06.kt).  
 
-<!--- TEST 
+```text
 kotlin.collections.LinkedHashMap(PrimitiveDescriptor(kotlin.String), Color(rgb: kotlin.Int))
---> 
+```
+
+<!--- TEST -->
 
 ## Custom serializers
 
 A plugin-generated serializer is convenient, but it may not produce the JSON we want 
-for such a class as `Color`. Let's study alternatives.
+for such a class as `Color`.
+Let's study the alternatives.
 
 ### Primitive serializer
 
@@ -253,7 +261,7 @@
 }
 ```
 
-Serializer has three required pieces. 
+A serializer has three required pieces. 
 
 * The [serialize][SerializationStrategy.serialize] function implements [SerializationStrategy].
   It receives an instance of [Encoder] and a value to serialize.
@@ -387,7 +395,7 @@
 Now let's see what our actions would be if we have to serialize `Color` as another non-primitive type, let's say `IntArray`.
 
 An implementation of [KSerializer] for our original `Color` class is going to perform a conversion between
-`Color` and `IntArray`, but delegate the actual serialization logic to the `IntArraySerializer`
+`Color` and `IntArray`, but delegate the actual serialization logic to `IntArraySerializer`
 using [encodeSerializableValue][Encoder.encodeSerializableValue] and
 [decodeSerializableValue][Decoder.decodeSerializableValue].
 
@@ -396,6 +404,7 @@
 
 class ColorIntArraySerializer : KSerializer<Color> {
     private val delegateSerializer = IntArraySerializer()
+    @OptIn(ExperimentalSerializationApi::class)
     override val descriptor = SerialDescriptor("Color", delegateSerializer.descriptor)
 
     override fun serialize(encoder: Encoder, value: Color) {
@@ -417,10 +426,10 @@
 Note that we can't use default `Color.serializer().descriptor` here because formats that rely
 on the schema may think that we would call `encodeInt` instead of `encodeSerializableValue`.
 Neither we can use `IntArraySerializer().descriptor` directly — otherwise, formats that handle int arrays specially
-can't tell if `value` is really a `IntArray` or a `Color`. Don't worry, this optimization would still kick in
-when serializing actual underlying int array.
+can't tell if `value` is really an `IntArray` or a `Color`.
+Don't worry, this optimization would still kick in when serializing the actual underlying int array.
 
-> Example of how format can treat arrays specially is shown in the [formats guide](formats.md#format-specific-types).
+> An example of how a format can treat arrays specially is shown in the [formats guide](formats.md#format-specific-types).
 
 Now we can use the serializer:
 
@@ -516,7 +525,7 @@
 
 <!--- TEST -->    
 
-### Hand-written composite serializer
+### Handwritten composite serializer
 
 There are some cases where a surrogate solution does not fit. Perhaps we want to avoid the performance 
 implications of additional allocation, or we want a configurable/dynamic set of properties for the 
@@ -615,10 +624,10 @@
 ### Sequential decoding protocol (experimental)
 
 The implementation of the `deserialize` function from the previous section works with any format. However,
-some formats either always store all the complex data in order, or only do so sometimes (JSON always stores
-collections in order). With these formats the complex protocol of calling `decodeElementIndex` in the loop is 
-not needed, and a faster implementation can be used if the [CompositeDecoder.decodeSequentially] function returns `true`.
-The plugin-generated serializers are actually conceptually similar to the below code.
+some formats either always store all the complex data in order or only do so sometimes (JSON always stores
+collections in order). With these formats the complex protocol of calling `decodeElementIndex` in a loop is 
+unnecessary, and a faster implementation can be used if the [CompositeDecoder.decodeSequentially] function returns `true`.
+The plugin-generated serializers are actually conceptually similar to the code below.
 
 <!--- INCLUDE
 object ColorAsObjectSerializer : KSerializer<Color> {
@@ -643,7 +652,8 @@
         decoder.decodeStructure(descriptor) {
             var r = -1
             var g = -1
-            var b = -1     
+            var b = -1
+            @OptIn(ExperimentalSerializationApi::class)
             if (decodeSequentially()) { // sequential decoding protocol
                 r = decodeIntElement(descriptor, 0)           
                 g = decodeIntElement(descriptor, 1)  
@@ -712,9 +722,15 @@
 because we don't control the `Date` source code. There are several ways to work around that.
 
 ### Passing a serializer manually
- 
-All `encodeToXxx` and `decodeFromXxx` functions have an overload with the first serializer parameter. 
-When a non-serializable class, like `Date`, is the top-level class being serialized, we can use those.
+
+The `encodeToXxx` and `decodeFromXxx` functions offer overloaded versions
+that accept either a [SerializationStrategy] or [DeserializationStrategy] as their first parameter, respectively.
+This feature allows you
+to provide a custom serializer for types that aren't annotated with [`@Serializable`][Serializable] by default.
+
+This approach is particularly useful
+when working with non-serializable classes like `Date` as the top-level object being serialized.
+Here's an example:
 
 ```kotlin
 fun main() {                                              
@@ -731,7 +747,7 @@
 
 <!--- TEST -->
 
-### Specifying serializer on a property
+### Specifying a serializer on a property
 
 When a property of a non-serializable class, like `Date`, is serialized as part of a serializable class we must supply
 its serializer or the code will not compile. This is accomplished using the [`@Serializable`][Serializable] annotation on the property.
@@ -771,7 +787,7 @@
 
 <!--- TEST -->
 
-### Specifying serializer for a particular type
+### Specifying a serializer for a particular type
 
 [`@Serializable`][Serializable] annotation can also be applied directly to the types. 
 This is handy when a class that requires a custom serializer, such as `Date`, happens to be a generic type argument.
@@ -810,7 +826,7 @@
 
 <!--- TEST -->
 
-### Specifying serializers for a file 
+### Specifying serializers for a file
 
 A serializer for a specific type, like `Date`, can be specified for a whole source code file with the file-level
 [UseSerializers] annotation at the beginning of the file.
@@ -851,7 +867,7 @@
 
 <!--- TEST --> 
 
-### Specifying serializer globally using typealias
+### Specifying a serializer globally using a typealias
 
 kotlinx.serialization tends to be the always-explicit framework when it comes to serialization strategies: normally,
 they should be explicitly mentioned in `@Serializable` annotation. Therefore, we do not provide any kind of global serializer
@@ -862,6 +878,7 @@
 For such cases, it is possible to specify serializers using `typealias`es, as they preserve annotations, including serialization-related ones:
 <!--- INCLUDE
 import java.util.Date
+import java.util.TimeZone
 import java.text.SimpleDateFormat
   
 object DateAsLongSerializer : KSerializer<Date> {
@@ -872,7 +889,11 @@
 
 object DateAsSimpleTextSerializer: KSerializer<Date> {
     override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("DateAsSimpleText", PrimitiveKind.LONG)
-    private val format = SimpleDateFormat("yyyy-MM-dd")
+    private val format = SimpleDateFormat("yyyy-MM-dd").apply {
+        // Here we explicitly set time zone to UTC so output for this sample remains locale-independent.
+        // Depending on your needs, you may have to adjust or remove this line.
+        setTimeZone(TimeZone.getTimeZone("UTC"))
+    }
     override fun serialize(encoder: Encoder, value: Date) = encoder.encodeString(format.format(value))
     override fun deserialize(decoder: Decoder): Date = format.parse(decoder.decodeString())
 }
@@ -970,6 +991,61 @@
   
 This chapter proceeds with a generic approach to tweaking the serialization strategy based on the context.   
 
+## Simultaneous use of plugin-generated and custom serializers
+In some cases it may be useful to have a serialization plugin continue to generate a serializer even if a custom one is used for the class.
+
+The most common examples are: using a plugin-generated serializer for fallback strategy, accessing type structure via [descriptor][KSerializer.descriptor] of plugin-generated serializer, use default serialization behavior in descendants that do not use custom serializers.
+
+In order for the plugin to continue generating the serializer, you must specify the `@KeepGeneratedSerializer` annotation in the type declaration.
+In this case, the serializer will be accessible using the `.generatedSerializer()` function on the class's companion object.
+
+> This annotation is currently experimental. Kotlin 2.0.20 or higher is required for this feature to work.
+
+Annotation `@KeepGeneratedSerializer` is not allowed on classes involved in polymorphic serialization: interfaces, sealed classes, abstract classes, classes marked by [Polymorphic].
+
+An example of using two serializers at once:
+
+<!--- INCLUDE
+object ColorAsStringSerializer : KSerializer<Color> {
+    override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Color", PrimitiveKind.STRING)
+
+    override fun serialize(encoder: Encoder, value: Color) {
+        val string = value.rgb.toString(16).padStart(6, '0')
+        encoder.encodeString(string)
+    }
+
+    override fun deserialize(decoder: Decoder): Color {
+        val string = decoder.decodeString()
+        return Color(string.toInt(16))
+    }
+}
+-->
+
+```kotlin
+@OptIn(ExperimentalSerializationApi::class)
+@KeepGeneratedSerializer
+@Serializable(with = ColorAsStringSerializer::class)
+class Color(val rgb: Int)
+
+
+fun main() {
+    val green = Color(0x00ff00)
+    println(Json.encodeToString(green))
+    println(Json.encodeToString(Color.generatedSerializer(), green))
+}  
+```  
+
+> You can get the full code [here](../guide/example/example-serializer-20.kt).
+
+As a result, serialization will occur using custom and plugin-generated serializers:
+
+```text
+"00ff00"
+{"rgb":65280}
+```
+
+<!--- TEST -->
+
 ## Contextual serialization
 
 All the previous approaches to specifying custom serialization strategies were _static_, that is 
@@ -1009,7 +1085,7 @@
 To actually serialize this class we must provide the corresponding context when calling the `encodeToXxx`/`decodeFromXxx`
 functions. Without it we'll get a "Serializer for class 'Date' is not found" exception.
 
-> See [here](../guide/example/example-serializer-20.kt) for an example that produces that exception.
+> See [here](../guide/example/example-serializer-21.kt) for an example that produces that exception.
  
 <!--- TEST LINES_START 
 Exception in thread "main" kotlinx.serialization.SerializationException: Serializer for class 'Date' is not found.
@@ -1040,7 +1116,7 @@
 To provide a context, we define a [SerializersModule] instance that describes which serializers shall be used 
 at run-time to serialize which contextually-serializable classes. This is done using the 
 [SerializersModule {}][SerializersModule()] builder function, which provides the [SerializersModuleBuilder] DSL to 
-register serializers. In the below example we use the [contextual][_contextual] function with the serializer. The corresponding
+register serializers. In the example below we use the [contextual][_contextual] function with the serializer. The corresponding
 class this serializer is defined for is fetched automatically via the `reified` type parameter.  
 
 ```kotlin
@@ -1068,7 +1144,7 @@
 }
 ```
 
-> You can get the full code [here](../guide/example/example-serializer-21.kt).
+> You can get the full code [here](../guide/example/example-serializer-22.kt).
 ```text
 {"name":"Kotlin","stableReleaseDate":1455494400000}
 ```
@@ -1112,7 +1188,8 @@
 ```kotlin         
 // NOT @Serializable
 class Project(val name: String, val language: String)
-                           
+
+@OptIn(ExperimentalSerializationApi::class)
 @Serializer(forClass = Project::class)
 object ProjectSerializer
 ```
@@ -1127,7 +1204,7 @@
 }
 ```          
 
-> You can get the full code [here](../guide/example/example-serializer-22.kt).
+> You can get the full code [here](../guide/example/example-serializer-23.kt).
 
 This gets all the `Project` properties serialized:
 
@@ -1137,7 +1214,7 @@
 
 <!--- TEST -->
 
-### External serialization uses properties 
+### External serialization uses properties
 
 As we saw earlier, the regular `@Serializable` annotation creates a serializer so that 
 [Backing fields are serialized](basic-serialization.md#backing-fields-are-serialized). _External_ serialization using 
@@ -1157,8 +1234,9 @@
         get() = "kotlin/$name"                                         
 
     private var locked: Boolean = false // private, not accessible -- not serialized 
-}              
+}
 
+@OptIn(ExperimentalSerializationApi::class)
 @Serializer(forClass = Project::class)
 object ProjectSerializer
 
@@ -1168,7 +1246,7 @@
 }
 ```             
 
-> You can get the full code [here](../guide/example/example-serializer-23.kt).
+> You can get the full code [here](../guide/example/example-serializer-24.kt).
 
 The output is shown below.
 
@@ -1198,6 +1276,7 @@
 [Serializable.with]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serializable/with.html
 [SerialName]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serial-name/index.html
 [UseSerializers]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-use-serializers/index.html
+[Polymorphic]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-polymorphic/index.html
 [ContextualSerializer]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-contextual-serializer/index.html
 [Contextual]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-contextual/index.html
 [UseContextualSerialization]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-use-contextual-serialization/index.html
diff --git a/dokka/moduledoc.md b/dokka/moduledoc.md
index cd0462d..6e70b5f 100644
--- a/dokka/moduledoc.md
+++ b/dokka/moduledoc.md
@@ -9,6 +9,10 @@
 Extensions for kotlinx.serialization.json.Json for integration with the popular [Okio](https://square.github.io/okio/) library.
 Currently experimental.
 
+# Module kotlinx-serialization-json-io
+Extensions for kotlinx.serialization.json.Json for integration with the [kotlinx-io](https://github.com/Kotlin/kotlinx-io) library.
+Currently experimental.
+
 # Module kotlinx-serialization-cbor
 Concise Binary Object Representation (CBOR) format implementation, as per [RFC 7049](https://tools.ietf.org/html/rfc7049).
 
@@ -49,6 +53,9 @@
 # Package kotlinx.serialization.json.okio
 Extensions for kotlinx.serialization.json.Json for integration with the popular [Okio](https://square.github.io/okio/) library.
 
+# Package kotlinx.serialization.json.io
+Extensions for kotlinx.serialization.json.Json for integration with the [kotlinx-io](https://github.com/Kotlin/kotlinx-io) library.
+
 # Package kotlinx.serialization.protobuf
 [Protocol buffers](https://protobuf.dev/) serialization format implementation.
 
diff --git a/formats/README.md b/formats/README.md
index 724b06a..62d59c4 100644
--- a/formats/README.md
+++ b/formats/README.md
@@ -18,7 +18,7 @@
 
 | Format                   | GitHub repo and Artifact                                                                                                                                 | Platform                | Notes                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
 |--------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------|-------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| Avro                     | [sksamuel/avro4k](https://github.com/sksamuel/avro4k) <br> `com.sksamuel.avro4k:avro4k`                                                                  | JVM only                | This library allows serialization and deserialization of objects to and from [Avro](https://avro.apache.org). It will read and write from Avro binary or json streams or generate Avro Generic Records directly. It will also generate Avro schemas from data classes. The library allows for easy extension and overrides for custom schema formats, compatiblity with schemas defined outside out of the JVM and for types not supported out of the box. |
+| Avro                     | [avro-kotlin/avro4k](https://github.com/avro-kotlin/avro4k) <br> `com.github.avro-kotlin.avro4k:avro4k-core`                                                                  | JVM only                | This library allows serialization and deserialization of objects to and from [Avro](https://avro.apache.org). It will read and write from Avro binary or json streams or generate Avro Generic Records directly. It will also generate Avro schemas from data classes. The library allows for easy extension and overrides for custom schema formats, compatiblity with schemas defined outside out of the JVM and for types not supported out of the box. |
 | Bson                     | [jershell/kbson](https://github.com/jershell/kbson) <br> `com.github.jershell:kbson`                                                                     | JVM only                | Allows serialization and deserialization of objects to and from [BSON](https://docs.mongodb.com/manual/reference/bson-types/).                                                                                                                                                                                                                                                                                                                             |
 | TOML                     | [Peanuuutz/tomlkt](https://github.com/Peanuuutz/tomlkt) <br> `net.peanuuutz.tomlkt:tomlkt`                                                               | all supported platforms | Multiplatform encoder and decoder for [TOML](http://toml.io/) 1.0.0 compliant. This library aims to provide similar API to the official JSON format (such as TomlLiteral, TomlTable), while adding TOML specific features (such as @TomlComment, @TomlMultilineString).                                                                                                                                                                                    |
 | TOML                     | [akuleshov7/ktoml](https://github.com/akuleshov7/ktoml) <br> `com.akuleshov7:ktoml-core`                                                                 | all supported platforms | Fully Native and Multiplatform Kotlin serialization library for serialization/deserialization of TOML format. This library contains no Java code and no Java dependencies and it implements multiplatform parser, decoder and encoder of TOML.                                                                                                                                                                                                             |
@@ -26,11 +26,15 @@
 | MsgPack                  | [esensar/kotlinx-serialization-msgpack](https://github.com/esensar/kotlinx-serialization-msgpack) <br> `com.ensarsarajcic.kotlinx:serialization-msgpack` | all supported platforms | Allows serialization and deserialization of objects to and from [MsgPack](https://msgpack.org/).                                                                                                                                                                                                                                                                                                                                                           |
 | SharedPreferences        | [EdwarDDay/serialization.kprefs](https://github.com/EdwarDDay/serialization.kprefs) <br> `net.edwardday.serialization:kprefs`                            | Android only            | This library allows serialization and deserialization of objects into and from Android [SharedPreferences](https://developer.android.com/reference/android/content/SharedPreferences).                                                                                                                                                                                                                                                                     |
 | XML                      | [pdvrieze/xmlutil](https://github.com/pdvrieze/xmlutil) <br> `io.github.pdvrieze.xmlutil:serialization`                                                  | all supported platforms | This library allows for reading and writing of XML documents with the serialization library. It is multiplatform, providing both a shared parser/writer for xml as well as platform-specific parsers where available. The library is designed to handle existing xml formats that use features that would not be available in other formats such as JSON.                                                                                                  |
-| YAML                     | [charleskorn/kaml](https://github.com/charleskorn/kaml) <br> `com.charleskorn.kaml:kaml`                                                                 | JVM only                | Allows serialization and deserialization of objects to and from [YAML](http://yaml.org).                                                                                                                                                                                                                                                                                                                                                                   |
+| YAML                     | [charleskorn/kaml](https://github.com/charleskorn/kaml) <br> `com.charleskorn.kaml:kaml`                                                                 | all supported platforms       | Allows serialization and deserialization of objects to and from [YAML](http://yaml.org).                                                                                                                                                                                                                                                                                                                                                                   |
 | YAML                     | [him188/yamlkt](https://github.com/him188/yamlkt) <br> `net.mamoe.yamlkt:yamlkt`                                                                         | all supported platforms | Allows serialization and deserialization of objects to and from [YAML](http://yaml.org). Basic serial operations have been implemented, but some features such as compound keys and polymorphism are still work in progress.                                                                                                                                                                                                                               |
-| CBOR                     | [L-Briand/obor](https://github.com/L-Briand/obor) <br> `net.orandja.obor:obor`                                                                           | JVM, Android            | Allow serialization and deserialization of objects to and from [CBOR](https://cbor.io/). This codec can be used to read and write from Java InputStream and OutputStream.                                                                                                                                                                                                                                                                                  |
+| CBOR                     | [L-Briand/obor](https://github.com/L-Briand/obor) <br> `net.orandja.obor:obor`                                                                           | all supported platforms | Allow serialization and deserialization of objects to and from [CBOR](https://cbor.io/). The serializer supports major type 2 byte string and custom tags.                                                                                                                                                                                                                                                                                 |
 | Amazon Ion (binary only) | [dimitark/kotlinx-serialization-ion](https://github.com/dimitark/kotlinx-serialization-ion) <br> `com.github.dimitark:kotlinx-serialization-ion`         | JVM only                | Allow serialization and deserialization of objects to and from [Amazon Ion](https://amzn.github.io/ion-docs/). It stores the data in a flat binary format. Upon destialization, it retains the references between the objects.                                                                                                                                                                                                                             |
 | android.os.Bundle        | [AhmedMourad0/bundlizer](https://github.com/AhmedMourad0/bundlizer) <br> `dev.ahmedmourad.bundlizer:bundlizer-core`                                      | Android                 | Allow serialization and deserialization of objects to and from [android.os.Bundle](https://developer.android.com/reference/android/os/Bundle).                                                                                                                                                                                                                                                                                                             |
 | CSV                      | [hfhbd/kotlinx-serialization-csv](https://github.com/hfhbd/kotlinx-serialization-csv) <br> `app.softwork:kotlinx-serialization-csv`                      | all supported platforms | Allows serialization and deserialization of CSV files. There are still some limitations (ordered properties).                                                                                                                                                                                                                                                                                                                                              |
 | Fixed Length Format      | [hfhbd/kotlinx-serialization-csv](https://github.com/hfhbd/kotlinx-serialization-csv) <br> `app.softwork:kotlinx-serialization-flf`                      | all supported platforms | Allows serialization and deserialization of [Fixed Length Format files](https://www.ibm.com/docs/en/psfa/7.2.1?topic=format-fixed-length-files). Each property must be annotated with `@FixedLength` and there are still some limitations due to missing delimiters.                                                                                                                                                                                       |
 | JSON5                    | [xn32/json5k](https://github.com/xn32/json5k) <br> `io.github.xn32:json5k`                                                                               | JVM, Native             | Library for the serialization to and deserialization from [JSON5](https://json5.org) text.                                                                                                                                                                                                                                                                                                                                                                 |
+| DynamoDB                 | [DynaMap](https://github.com/codanbaru/dynamap) <br> `com.codanbaru.kotlin:dynamap`                                                                      | JVM                     | Allows serialization and deserialization of objects to and from [AttributeValue](https://sdk.amazonaws.com/kotlin/api/latest/dynamodb/aws.sdk.kotlin.services.dynamodb.model/-attribute-value/index.html) of Amazon [DynamoDB](https://aws.amazon.com/dynamodb/)                                                                                                                                                                                           |
+| Bencoding                | [iseki0/kotlinx-serialization-bencoding](https://github.com/iseki0/kotlinx-serialization-bencoding) <br> `space.iseki.bencoding:kotlinx-serialization-bencoding`| JVM, JavaScript  | Allows serialization and deserialization of objects to and from [Bencoding](https://www.bittorrent.org/beps/bep_0003.html#bencoding) of BitTorrent.                                                                                                                                                                                                                                                                                                        |
+| Smile                    | [vooft/kotlinx-serialization-smile](https://github.com/vooft/kotlinx-serialization-smile) <br> `io.github.vooft:kotlinx-serialization-smile-core`        | all supported platforms | Allows serialization and deserialization of objects to and from [Smile](https://en.wikipedia.org/wiki/Smile_(data_interchange_format)).                                                                                                                                                                                                                                                                                                                    |
+
diff --git a/formats/cbor/api/kotlinx-serialization-cbor.api b/formats/cbor/api/kotlinx-serialization-cbor.api
index cc75fab..8b58032 100644
--- a/formats/cbor/api/kotlinx-serialization-cbor.api
+++ b/formats/cbor/api/kotlinx-serialization-cbor.api
@@ -7,22 +7,84 @@
 
 public abstract class kotlinx/serialization/cbor/Cbor : kotlinx/serialization/BinaryFormat {
 	public static final field Default Lkotlinx/serialization/cbor/Cbor$Default;
-	public synthetic fun <init> (ZZLkotlinx/serialization/modules/SerializersModule;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
+	public synthetic fun <init> (Lkotlinx/serialization/cbor/CborConfiguration;Lkotlinx/serialization/modules/SerializersModule;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
 	public fun decodeFromByteArray (Lkotlinx/serialization/DeserializationStrategy;[B)Ljava/lang/Object;
 	public fun encodeToByteArray (Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)[B
+	public final fun getConfiguration ()Lkotlinx/serialization/cbor/CborConfiguration;
 	public fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule;
 }
 
 public final class kotlinx/serialization/cbor/Cbor$Default : kotlinx/serialization/cbor/Cbor {
+	public final fun getCoseCompliant ()Lkotlinx/serialization/cbor/Cbor;
+}
+
+public abstract interface annotation class kotlinx/serialization/cbor/CborArray : java/lang/annotation/Annotation {
+}
+
+public synthetic class kotlinx/serialization/cbor/CborArray$Impl : kotlinx/serialization/cbor/CborArray {
+	public fun <init> ()V
 }
 
 public final class kotlinx/serialization/cbor/CborBuilder {
+	public final fun getAlwaysUseByteString ()Z
 	public final fun getEncodeDefaults ()Z
+	public final fun getEncodeKeyTags ()Z
+	public final fun getEncodeObjectTags ()Z
+	public final fun getEncodeValueTags ()Z
 	public final fun getIgnoreUnknownKeys ()Z
+	public final fun getPreferCborLabelsOverNames ()Z
 	public final fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule;
+	public final fun getUseDefiniteLengthEncoding ()Z
+	public final fun getVerifyKeyTags ()Z
+	public final fun getVerifyObjectTags ()Z
+	public final fun getVerifyValueTags ()Z
+	public final fun setAlwaysUseByteString (Z)V
 	public final fun setEncodeDefaults (Z)V
+	public final fun setEncodeKeyTags (Z)V
+	public final fun setEncodeObjectTags (Z)V
+	public final fun setEncodeValueTags (Z)V
 	public final fun setIgnoreUnknownKeys (Z)V
+	public final fun setPreferCborLabelsOverNames (Z)V
 	public final fun setSerializersModule (Lkotlinx/serialization/modules/SerializersModule;)V
+	public final fun setUseDefiniteLengthEncoding (Z)V
+	public final fun setVerifyKeyTags (Z)V
+	public final fun setVerifyObjectTags (Z)V
+	public final fun setVerifyValueTags (Z)V
+}
+
+public final class kotlinx/serialization/cbor/CborConfiguration {
+	public final fun getAlwaysUseByteString ()Z
+	public final fun getEncodeDefaults ()Z
+	public final fun getEncodeKeyTags ()Z
+	public final fun getEncodeObjectTags ()Z
+	public final fun getEncodeValueTags ()Z
+	public final fun getIgnoreUnknownKeys ()Z
+	public final fun getPreferCborLabelsOverNames ()Z
+	public final fun getUseDefiniteLengthEncoding ()Z
+	public final fun getVerifyKeyTags ()Z
+	public final fun getVerifyObjectTags ()Z
+	public final fun getVerifyValueTags ()Z
+	public fun toString ()Ljava/lang/String;
+}
+
+public abstract interface class kotlinx/serialization/cbor/CborDecoder : kotlinx/serialization/encoding/Decoder {
+	public abstract fun getCbor ()Lkotlinx/serialization/cbor/Cbor;
+}
+
+public final class kotlinx/serialization/cbor/CborDecoder$DefaultImpls {
+	public static fun decodeNullableSerializableValue (Lkotlinx/serialization/cbor/CborDecoder;Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object;
+	public static fun decodeSerializableValue (Lkotlinx/serialization/cbor/CborDecoder;Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object;
+}
+
+public abstract interface class kotlinx/serialization/cbor/CborEncoder : kotlinx/serialization/encoding/Encoder {
+	public abstract fun getCbor ()Lkotlinx/serialization/cbor/Cbor;
+}
+
+public final class kotlinx/serialization/cbor/CborEncoder$DefaultImpls {
+	public static fun beginCollection (Lkotlinx/serialization/cbor/CborEncoder;Lkotlinx/serialization/descriptors/SerialDescriptor;I)Lkotlinx/serialization/encoding/CompositeEncoder;
+	public static fun encodeNotNullMark (Lkotlinx/serialization/cbor/CborEncoder;)V
+	public static fun encodeNullableSerializableValue (Lkotlinx/serialization/cbor/CborEncoder;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V
+	public static fun encodeSerializableValue (Lkotlinx/serialization/cbor/CborEncoder;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;)V
 }
 
 public final class kotlinx/serialization/cbor/CborKt {
@@ -30,3 +92,59 @@
 	public static synthetic fun Cbor$default (Lkotlinx/serialization/cbor/Cbor;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlinx/serialization/cbor/Cbor;
 }
 
+public abstract interface annotation class kotlinx/serialization/cbor/CborLabel : java/lang/annotation/Annotation {
+	public abstract fun label ()J
+}
+
+public synthetic class kotlinx/serialization/cbor/CborLabel$Impl : kotlinx/serialization/cbor/CborLabel {
+	public fun <init> (J)V
+	public final synthetic fun label ()J
+}
+
+public final class kotlinx/serialization/cbor/CborTag {
+	public static final field BASE16 J
+	public static final field BASE64 J
+	public static final field BASE64_URL J
+	public static final field BIGFLOAT J
+	public static final field BIGNUM_NEGAIVE J
+	public static final field BIGNUM_POSITIVE J
+	public static final field CBOR_ENCODED_DATA J
+	public static final field CBOR_SELF_DESCRIBE J
+	public static final field DATE_TIME_EPOCH J
+	public static final field DATE_TIME_STANDARD J
+	public static final field DECIMAL_FRACTION J
+	public static final field INSTANCE Lkotlinx/serialization/cbor/CborTag;
+	public static final field MIME_MESSAGE J
+	public static final field REGEX J
+	public static final field STRING_BASE64 J
+	public static final field STRING_BASE64_URL J
+	public static final field URI J
+}
+
+public abstract interface annotation class kotlinx/serialization/cbor/KeyTags : java/lang/annotation/Annotation {
+	public abstract fun tags ()[J
+}
+
+public synthetic class kotlinx/serialization/cbor/KeyTags$Impl : kotlinx/serialization/cbor/KeyTags {
+	public synthetic fun <init> ([JLkotlin/jvm/internal/DefaultConstructorMarker;)V
+	public final synthetic fun tags ()[J
+}
+
+public abstract interface annotation class kotlinx/serialization/cbor/ObjectTags : java/lang/annotation/Annotation {
+	public abstract fun tags ()[J
+}
+
+public synthetic class kotlinx/serialization/cbor/ObjectTags$Impl : kotlinx/serialization/cbor/ObjectTags {
+	public synthetic fun <init> ([JLkotlin/jvm/internal/DefaultConstructorMarker;)V
+	public final synthetic fun tags ()[J
+}
+
+public abstract interface annotation class kotlinx/serialization/cbor/ValueTags : java/lang/annotation/Annotation {
+	public abstract fun tags ()[J
+}
+
+public synthetic class kotlinx/serialization/cbor/ValueTags$Impl : kotlinx/serialization/cbor/ValueTags {
+	public synthetic fun <init> ([JLkotlin/jvm/internal/DefaultConstructorMarker;)V
+	public final synthetic fun tags ()[J
+}
+
diff --git a/formats/cbor/api/kotlinx-serialization-cbor.klib.api b/formats/cbor/api/kotlinx-serialization-cbor.klib.api
new file mode 100644
index 0000000..2658e35
--- /dev/null
+++ b/formats/cbor/api/kotlinx-serialization-cbor.klib.api
@@ -0,0 +1,171 @@
+// Klib ABI Dump
+// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, js, linuxArm32Hfp, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, wasmJs, wasmWasi, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64]
+// Rendering settings:
+// - Signature version: 2
+// - Show manifest properties: true
+// - Show declarations: true
+
+// Library unique name: <org.jetbrains.kotlinx:kotlinx-serialization-cbor>
+open annotation class kotlinx.serialization.cbor/ByteString : kotlin/Annotation { // kotlinx.serialization.cbor/ByteString|null[0]
+    constructor <init>() // kotlinx.serialization.cbor/ByteString.<init>|<init>(){}[0]
+}
+
+open annotation class kotlinx.serialization.cbor/CborArray : kotlin/Annotation { // kotlinx.serialization.cbor/CborArray|null[0]
+    constructor <init>() // kotlinx.serialization.cbor/CborArray.<init>|<init>(){}[0]
+}
+
+open annotation class kotlinx.serialization.cbor/CborLabel : kotlin/Annotation { // kotlinx.serialization.cbor/CborLabel|null[0]
+    constructor <init>(kotlin/Long) // kotlinx.serialization.cbor/CborLabel.<init>|<init>(kotlin.Long){}[0]
+
+    final val label // kotlinx.serialization.cbor/CborLabel.label|{}label[0]
+        final fun <get-label>(): kotlin/Long // kotlinx.serialization.cbor/CborLabel.label.<get-label>|<get-label>(){}[0]
+}
+
+open annotation class kotlinx.serialization.cbor/KeyTags : kotlin/Annotation { // kotlinx.serialization.cbor/KeyTags|null[0]
+    constructor <init>(kotlin/ULongArray...) // kotlinx.serialization.cbor/KeyTags.<init>|<init>(kotlin.ULongArray...){}[0]
+
+    final val tags // kotlinx.serialization.cbor/KeyTags.tags|{}tags[0]
+        final fun <get-tags>(): kotlin/ULongArray // kotlinx.serialization.cbor/KeyTags.tags.<get-tags>|<get-tags>(){}[0]
+}
+
+open annotation class kotlinx.serialization.cbor/ObjectTags : kotlin/Annotation { // kotlinx.serialization.cbor/ObjectTags|null[0]
+    constructor <init>(kotlin/ULongArray...) // kotlinx.serialization.cbor/ObjectTags.<init>|<init>(kotlin.ULongArray...){}[0]
+
+    final val tags // kotlinx.serialization.cbor/ObjectTags.tags|{}tags[0]
+        final fun <get-tags>(): kotlin/ULongArray // kotlinx.serialization.cbor/ObjectTags.tags.<get-tags>|<get-tags>(){}[0]
+}
+
+open annotation class kotlinx.serialization.cbor/ValueTags : kotlin/Annotation { // kotlinx.serialization.cbor/ValueTags|null[0]
+    constructor <init>(kotlin/ULongArray...) // kotlinx.serialization.cbor/ValueTags.<init>|<init>(kotlin.ULongArray...){}[0]
+
+    final val tags // kotlinx.serialization.cbor/ValueTags.tags|{}tags[0]
+        final fun <get-tags>(): kotlin/ULongArray // kotlinx.serialization.cbor/ValueTags.tags.<get-tags>|<get-tags>(){}[0]
+}
+
+abstract interface kotlinx.serialization.cbor/CborDecoder : kotlinx.serialization.encoding/Decoder { // kotlinx.serialization.cbor/CborDecoder|null[0]
+    abstract val cbor // kotlinx.serialization.cbor/CborDecoder.cbor|{}cbor[0]
+        abstract fun <get-cbor>(): kotlinx.serialization.cbor/Cbor // kotlinx.serialization.cbor/CborDecoder.cbor.<get-cbor>|<get-cbor>(){}[0]
+}
+
+abstract interface kotlinx.serialization.cbor/CborEncoder : kotlinx.serialization.encoding/Encoder { // kotlinx.serialization.cbor/CborEncoder|null[0]
+    abstract val cbor // kotlinx.serialization.cbor/CborEncoder.cbor|{}cbor[0]
+        abstract fun <get-cbor>(): kotlinx.serialization.cbor/Cbor // kotlinx.serialization.cbor/CborEncoder.cbor.<get-cbor>|<get-cbor>(){}[0]
+}
+
+final class kotlinx.serialization.cbor/CborBuilder { // kotlinx.serialization.cbor/CborBuilder|null[0]
+    final var alwaysUseByteString // kotlinx.serialization.cbor/CborBuilder.alwaysUseByteString|{}alwaysUseByteString[0]
+        final fun <get-alwaysUseByteString>(): kotlin/Boolean // kotlinx.serialization.cbor/CborBuilder.alwaysUseByteString.<get-alwaysUseByteString>|<get-alwaysUseByteString>(){}[0]
+        final fun <set-alwaysUseByteString>(kotlin/Boolean) // kotlinx.serialization.cbor/CborBuilder.alwaysUseByteString.<set-alwaysUseByteString>|<set-alwaysUseByteString>(kotlin.Boolean){}[0]
+    final var encodeDefaults // kotlinx.serialization.cbor/CborBuilder.encodeDefaults|{}encodeDefaults[0]
+        final fun <get-encodeDefaults>(): kotlin/Boolean // kotlinx.serialization.cbor/CborBuilder.encodeDefaults.<get-encodeDefaults>|<get-encodeDefaults>(){}[0]
+        final fun <set-encodeDefaults>(kotlin/Boolean) // kotlinx.serialization.cbor/CborBuilder.encodeDefaults.<set-encodeDefaults>|<set-encodeDefaults>(kotlin.Boolean){}[0]
+    final var encodeKeyTags // kotlinx.serialization.cbor/CborBuilder.encodeKeyTags|{}encodeKeyTags[0]
+        final fun <get-encodeKeyTags>(): kotlin/Boolean // kotlinx.serialization.cbor/CborBuilder.encodeKeyTags.<get-encodeKeyTags>|<get-encodeKeyTags>(){}[0]
+        final fun <set-encodeKeyTags>(kotlin/Boolean) // kotlinx.serialization.cbor/CborBuilder.encodeKeyTags.<set-encodeKeyTags>|<set-encodeKeyTags>(kotlin.Boolean){}[0]
+    final var encodeObjectTags // kotlinx.serialization.cbor/CborBuilder.encodeObjectTags|{}encodeObjectTags[0]
+        final fun <get-encodeObjectTags>(): kotlin/Boolean // kotlinx.serialization.cbor/CborBuilder.encodeObjectTags.<get-encodeObjectTags>|<get-encodeObjectTags>(){}[0]
+        final fun <set-encodeObjectTags>(kotlin/Boolean) // kotlinx.serialization.cbor/CborBuilder.encodeObjectTags.<set-encodeObjectTags>|<set-encodeObjectTags>(kotlin.Boolean){}[0]
+    final var encodeValueTags // kotlinx.serialization.cbor/CborBuilder.encodeValueTags|{}encodeValueTags[0]
+        final fun <get-encodeValueTags>(): kotlin/Boolean // kotlinx.serialization.cbor/CborBuilder.encodeValueTags.<get-encodeValueTags>|<get-encodeValueTags>(){}[0]
+        final fun <set-encodeValueTags>(kotlin/Boolean) // kotlinx.serialization.cbor/CborBuilder.encodeValueTags.<set-encodeValueTags>|<set-encodeValueTags>(kotlin.Boolean){}[0]
+    final var ignoreUnknownKeys // kotlinx.serialization.cbor/CborBuilder.ignoreUnknownKeys|{}ignoreUnknownKeys[0]
+        final fun <get-ignoreUnknownKeys>(): kotlin/Boolean // kotlinx.serialization.cbor/CborBuilder.ignoreUnknownKeys.<get-ignoreUnknownKeys>|<get-ignoreUnknownKeys>(){}[0]
+        final fun <set-ignoreUnknownKeys>(kotlin/Boolean) // kotlinx.serialization.cbor/CborBuilder.ignoreUnknownKeys.<set-ignoreUnknownKeys>|<set-ignoreUnknownKeys>(kotlin.Boolean){}[0]
+    final var preferCborLabelsOverNames // kotlinx.serialization.cbor/CborBuilder.preferCborLabelsOverNames|{}preferCborLabelsOverNames[0]
+        final fun <get-preferCborLabelsOverNames>(): kotlin/Boolean // kotlinx.serialization.cbor/CborBuilder.preferCborLabelsOverNames.<get-preferCborLabelsOverNames>|<get-preferCborLabelsOverNames>(){}[0]
+        final fun <set-preferCborLabelsOverNames>(kotlin/Boolean) // kotlinx.serialization.cbor/CborBuilder.preferCborLabelsOverNames.<set-preferCborLabelsOverNames>|<set-preferCborLabelsOverNames>(kotlin.Boolean){}[0]
+    final var serializersModule // kotlinx.serialization.cbor/CborBuilder.serializersModule|{}serializersModule[0]
+        final fun <get-serializersModule>(): kotlinx.serialization.modules/SerializersModule // kotlinx.serialization.cbor/CborBuilder.serializersModule.<get-serializersModule>|<get-serializersModule>(){}[0]
+        final fun <set-serializersModule>(kotlinx.serialization.modules/SerializersModule) // kotlinx.serialization.cbor/CborBuilder.serializersModule.<set-serializersModule>|<set-serializersModule>(kotlinx.serialization.modules.SerializersModule){}[0]
+    final var useDefiniteLengthEncoding // kotlinx.serialization.cbor/CborBuilder.useDefiniteLengthEncoding|{}useDefiniteLengthEncoding[0]
+        final fun <get-useDefiniteLengthEncoding>(): kotlin/Boolean // kotlinx.serialization.cbor/CborBuilder.useDefiniteLengthEncoding.<get-useDefiniteLengthEncoding>|<get-useDefiniteLengthEncoding>(){}[0]
+        final fun <set-useDefiniteLengthEncoding>(kotlin/Boolean) // kotlinx.serialization.cbor/CborBuilder.useDefiniteLengthEncoding.<set-useDefiniteLengthEncoding>|<set-useDefiniteLengthEncoding>(kotlin.Boolean){}[0]
+    final var verifyKeyTags // kotlinx.serialization.cbor/CborBuilder.verifyKeyTags|{}verifyKeyTags[0]
+        final fun <get-verifyKeyTags>(): kotlin/Boolean // kotlinx.serialization.cbor/CborBuilder.verifyKeyTags.<get-verifyKeyTags>|<get-verifyKeyTags>(){}[0]
+        final fun <set-verifyKeyTags>(kotlin/Boolean) // kotlinx.serialization.cbor/CborBuilder.verifyKeyTags.<set-verifyKeyTags>|<set-verifyKeyTags>(kotlin.Boolean){}[0]
+    final var verifyObjectTags // kotlinx.serialization.cbor/CborBuilder.verifyObjectTags|{}verifyObjectTags[0]
+        final fun <get-verifyObjectTags>(): kotlin/Boolean // kotlinx.serialization.cbor/CborBuilder.verifyObjectTags.<get-verifyObjectTags>|<get-verifyObjectTags>(){}[0]
+        final fun <set-verifyObjectTags>(kotlin/Boolean) // kotlinx.serialization.cbor/CborBuilder.verifyObjectTags.<set-verifyObjectTags>|<set-verifyObjectTags>(kotlin.Boolean){}[0]
+    final var verifyValueTags // kotlinx.serialization.cbor/CborBuilder.verifyValueTags|{}verifyValueTags[0]
+        final fun <get-verifyValueTags>(): kotlin/Boolean // kotlinx.serialization.cbor/CborBuilder.verifyValueTags.<get-verifyValueTags>|<get-verifyValueTags>(){}[0]
+        final fun <set-verifyValueTags>(kotlin/Boolean) // kotlinx.serialization.cbor/CborBuilder.verifyValueTags.<set-verifyValueTags>|<set-verifyValueTags>(kotlin.Boolean){}[0]
+}
+
+final class kotlinx.serialization.cbor/CborConfiguration { // kotlinx.serialization.cbor/CborConfiguration|null[0]
+    final val alwaysUseByteString // kotlinx.serialization.cbor/CborConfiguration.alwaysUseByteString|{}alwaysUseByteString[0]
+        final fun <get-alwaysUseByteString>(): kotlin/Boolean // kotlinx.serialization.cbor/CborConfiguration.alwaysUseByteString.<get-alwaysUseByteString>|<get-alwaysUseByteString>(){}[0]
+    final val encodeDefaults // kotlinx.serialization.cbor/CborConfiguration.encodeDefaults|{}encodeDefaults[0]
+        final fun <get-encodeDefaults>(): kotlin/Boolean // kotlinx.serialization.cbor/CborConfiguration.encodeDefaults.<get-encodeDefaults>|<get-encodeDefaults>(){}[0]
+    final val encodeKeyTags // kotlinx.serialization.cbor/CborConfiguration.encodeKeyTags|{}encodeKeyTags[0]
+        final fun <get-encodeKeyTags>(): kotlin/Boolean // kotlinx.serialization.cbor/CborConfiguration.encodeKeyTags.<get-encodeKeyTags>|<get-encodeKeyTags>(){}[0]
+    final val encodeObjectTags // kotlinx.serialization.cbor/CborConfiguration.encodeObjectTags|{}encodeObjectTags[0]
+        final fun <get-encodeObjectTags>(): kotlin/Boolean // kotlinx.serialization.cbor/CborConfiguration.encodeObjectTags.<get-encodeObjectTags>|<get-encodeObjectTags>(){}[0]
+    final val encodeValueTags // kotlinx.serialization.cbor/CborConfiguration.encodeValueTags|{}encodeValueTags[0]
+        final fun <get-encodeValueTags>(): kotlin/Boolean // kotlinx.serialization.cbor/CborConfiguration.encodeValueTags.<get-encodeValueTags>|<get-encodeValueTags>(){}[0]
+    final val ignoreUnknownKeys // kotlinx.serialization.cbor/CborConfiguration.ignoreUnknownKeys|{}ignoreUnknownKeys[0]
+        final fun <get-ignoreUnknownKeys>(): kotlin/Boolean // kotlinx.serialization.cbor/CborConfiguration.ignoreUnknownKeys.<get-ignoreUnknownKeys>|<get-ignoreUnknownKeys>(){}[0]
+    final val preferCborLabelsOverNames // kotlinx.serialization.cbor/CborConfiguration.preferCborLabelsOverNames|{}preferCborLabelsOverNames[0]
+        final fun <get-preferCborLabelsOverNames>(): kotlin/Boolean // kotlinx.serialization.cbor/CborConfiguration.preferCborLabelsOverNames.<get-preferCborLabelsOverNames>|<get-preferCborLabelsOverNames>(){}[0]
+    final val useDefiniteLengthEncoding // kotlinx.serialization.cbor/CborConfiguration.useDefiniteLengthEncoding|{}useDefiniteLengthEncoding[0]
+        final fun <get-useDefiniteLengthEncoding>(): kotlin/Boolean // kotlinx.serialization.cbor/CborConfiguration.useDefiniteLengthEncoding.<get-useDefiniteLengthEncoding>|<get-useDefiniteLengthEncoding>(){}[0]
+    final val verifyKeyTags // kotlinx.serialization.cbor/CborConfiguration.verifyKeyTags|{}verifyKeyTags[0]
+        final fun <get-verifyKeyTags>(): kotlin/Boolean // kotlinx.serialization.cbor/CborConfiguration.verifyKeyTags.<get-verifyKeyTags>|<get-verifyKeyTags>(){}[0]
+    final val verifyObjectTags // kotlinx.serialization.cbor/CborConfiguration.verifyObjectTags|{}verifyObjectTags[0]
+        final fun <get-verifyObjectTags>(): kotlin/Boolean // kotlinx.serialization.cbor/CborConfiguration.verifyObjectTags.<get-verifyObjectTags>|<get-verifyObjectTags>(){}[0]
+    final val verifyValueTags // kotlinx.serialization.cbor/CborConfiguration.verifyValueTags|{}verifyValueTags[0]
+        final fun <get-verifyValueTags>(): kotlin/Boolean // kotlinx.serialization.cbor/CborConfiguration.verifyValueTags.<get-verifyValueTags>|<get-verifyValueTags>(){}[0]
+
+    final fun toString(): kotlin/String // kotlinx.serialization.cbor/CborConfiguration.toString|toString(){}[0]
+}
+
+sealed class kotlinx.serialization.cbor/Cbor : kotlinx.serialization/BinaryFormat { // kotlinx.serialization.cbor/Cbor|null[0]
+    final val configuration // kotlinx.serialization.cbor/Cbor.configuration|{}configuration[0]
+        final fun <get-configuration>(): kotlinx.serialization.cbor/CborConfiguration // kotlinx.serialization.cbor/Cbor.configuration.<get-configuration>|<get-configuration>(){}[0]
+    open val serializersModule // kotlinx.serialization.cbor/Cbor.serializersModule|{}serializersModule[0]
+        open fun <get-serializersModule>(): kotlinx.serialization.modules/SerializersModule // kotlinx.serialization.cbor/Cbor.serializersModule.<get-serializersModule>|<get-serializersModule>(){}[0]
+
+    open fun <#A1: kotlin/Any?> decodeFromByteArray(kotlinx.serialization/DeserializationStrategy<#A1>, kotlin/ByteArray): #A1 // kotlinx.serialization.cbor/Cbor.decodeFromByteArray|decodeFromByteArray(kotlinx.serialization.DeserializationStrategy<0:0>;kotlin.ByteArray){0§<kotlin.Any?>}[0]
+    open fun <#A1: kotlin/Any?> encodeToByteArray(kotlinx.serialization/SerializationStrategy<#A1>, #A1): kotlin/ByteArray // kotlinx.serialization.cbor/Cbor.encodeToByteArray|encodeToByteArray(kotlinx.serialization.SerializationStrategy<0:0>;0:0){0§<kotlin.Any?>}[0]
+
+    final object Default : kotlinx.serialization.cbor/Cbor { // kotlinx.serialization.cbor/Cbor.Default|null[0]
+        final val CoseCompliant // kotlinx.serialization.cbor/Cbor.Default.CoseCompliant|{}CoseCompliant[0]
+            final fun <get-CoseCompliant>(): kotlinx.serialization.cbor/Cbor // kotlinx.serialization.cbor/Cbor.Default.CoseCompliant.<get-CoseCompliant>|<get-CoseCompliant>(){}[0]
+    }
+}
+
+final object kotlinx.serialization.cbor/CborTag { // kotlinx.serialization.cbor/CborTag|null[0]
+    final const val BASE16 // kotlinx.serialization.cbor/CborTag.BASE16|{}BASE16[0]
+        final fun <get-BASE16>(): kotlin/ULong // kotlinx.serialization.cbor/CborTag.BASE16.<get-BASE16>|<get-BASE16>(){}[0]
+    final const val BASE64 // kotlinx.serialization.cbor/CborTag.BASE64|{}BASE64[0]
+        final fun <get-BASE64>(): kotlin/ULong // kotlinx.serialization.cbor/CborTag.BASE64.<get-BASE64>|<get-BASE64>(){}[0]
+    final const val BASE64_URL // kotlinx.serialization.cbor/CborTag.BASE64_URL|{}BASE64_URL[0]
+        final fun <get-BASE64_URL>(): kotlin/ULong // kotlinx.serialization.cbor/CborTag.BASE64_URL.<get-BASE64_URL>|<get-BASE64_URL>(){}[0]
+    final const val BIGFLOAT // kotlinx.serialization.cbor/CborTag.BIGFLOAT|{}BIGFLOAT[0]
+        final fun <get-BIGFLOAT>(): kotlin/ULong // kotlinx.serialization.cbor/CborTag.BIGFLOAT.<get-BIGFLOAT>|<get-BIGFLOAT>(){}[0]
+    final const val BIGNUM_NEGAIVE // kotlinx.serialization.cbor/CborTag.BIGNUM_NEGAIVE|{}BIGNUM_NEGAIVE[0]
+        final fun <get-BIGNUM_NEGAIVE>(): kotlin/ULong // kotlinx.serialization.cbor/CborTag.BIGNUM_NEGAIVE.<get-BIGNUM_NEGAIVE>|<get-BIGNUM_NEGAIVE>(){}[0]
+    final const val BIGNUM_POSITIVE // kotlinx.serialization.cbor/CborTag.BIGNUM_POSITIVE|{}BIGNUM_POSITIVE[0]
+        final fun <get-BIGNUM_POSITIVE>(): kotlin/ULong // kotlinx.serialization.cbor/CborTag.BIGNUM_POSITIVE.<get-BIGNUM_POSITIVE>|<get-BIGNUM_POSITIVE>(){}[0]
+    final const val CBOR_ENCODED_DATA // kotlinx.serialization.cbor/CborTag.CBOR_ENCODED_DATA|{}CBOR_ENCODED_DATA[0]
+        final fun <get-CBOR_ENCODED_DATA>(): kotlin/ULong // kotlinx.serialization.cbor/CborTag.CBOR_ENCODED_DATA.<get-CBOR_ENCODED_DATA>|<get-CBOR_ENCODED_DATA>(){}[0]
+    final const val CBOR_SELF_DESCRIBE // kotlinx.serialization.cbor/CborTag.CBOR_SELF_DESCRIBE|{}CBOR_SELF_DESCRIBE[0]
+        final fun <get-CBOR_SELF_DESCRIBE>(): kotlin/ULong // kotlinx.serialization.cbor/CborTag.CBOR_SELF_DESCRIBE.<get-CBOR_SELF_DESCRIBE>|<get-CBOR_SELF_DESCRIBE>(){}[0]
+    final const val DATE_TIME_EPOCH // kotlinx.serialization.cbor/CborTag.DATE_TIME_EPOCH|{}DATE_TIME_EPOCH[0]
+        final fun <get-DATE_TIME_EPOCH>(): kotlin/ULong // kotlinx.serialization.cbor/CborTag.DATE_TIME_EPOCH.<get-DATE_TIME_EPOCH>|<get-DATE_TIME_EPOCH>(){}[0]
+    final const val DATE_TIME_STANDARD // kotlinx.serialization.cbor/CborTag.DATE_TIME_STANDARD|{}DATE_TIME_STANDARD[0]
+        final fun <get-DATE_TIME_STANDARD>(): kotlin/ULong // kotlinx.serialization.cbor/CborTag.DATE_TIME_STANDARD.<get-DATE_TIME_STANDARD>|<get-DATE_TIME_STANDARD>(){}[0]
+    final const val DECIMAL_FRACTION // kotlinx.serialization.cbor/CborTag.DECIMAL_FRACTION|{}DECIMAL_FRACTION[0]
+        final fun <get-DECIMAL_FRACTION>(): kotlin/ULong // kotlinx.serialization.cbor/CborTag.DECIMAL_FRACTION.<get-DECIMAL_FRACTION>|<get-DECIMAL_FRACTION>(){}[0]
+    final const val MIME_MESSAGE // kotlinx.serialization.cbor/CborTag.MIME_MESSAGE|{}MIME_MESSAGE[0]
+        final fun <get-MIME_MESSAGE>(): kotlin/ULong // kotlinx.serialization.cbor/CborTag.MIME_MESSAGE.<get-MIME_MESSAGE>|<get-MIME_MESSAGE>(){}[0]
+    final const val REGEX // kotlinx.serialization.cbor/CborTag.REGEX|{}REGEX[0]
+        final fun <get-REGEX>(): kotlin/ULong // kotlinx.serialization.cbor/CborTag.REGEX.<get-REGEX>|<get-REGEX>(){}[0]
+    final const val STRING_BASE64 // kotlinx.serialization.cbor/CborTag.STRING_BASE64|{}STRING_BASE64[0]
+        final fun <get-STRING_BASE64>(): kotlin/ULong // kotlinx.serialization.cbor/CborTag.STRING_BASE64.<get-STRING_BASE64>|<get-STRING_BASE64>(){}[0]
+    final const val STRING_BASE64_URL // kotlinx.serialization.cbor/CborTag.STRING_BASE64_URL|{}STRING_BASE64_URL[0]
+        final fun <get-STRING_BASE64_URL>(): kotlin/ULong // kotlinx.serialization.cbor/CborTag.STRING_BASE64_URL.<get-STRING_BASE64_URL>|<get-STRING_BASE64_URL>(){}[0]
+    final const val URI // kotlinx.serialization.cbor/CborTag.URI|{}URI[0]
+        final fun <get-URI>(): kotlin/ULong // kotlinx.serialization.cbor/CborTag.URI.<get-URI>|<get-URI>(){}[0]
+}
+
+final fun kotlinx.serialization.cbor/Cbor(kotlinx.serialization.cbor/Cbor = ..., kotlin/Function1<kotlinx.serialization.cbor/CborBuilder, kotlin/Unit>): kotlinx.serialization.cbor/Cbor // kotlinx.serialization.cbor/Cbor|Cbor(kotlinx.serialization.cbor.Cbor;kotlin.Function1<kotlinx.serialization.cbor.CborBuilder,kotlin.Unit>){}[0]
diff --git a/formats/cbor/build.gradle b/formats/cbor/build.gradle
deleted file mode 100644
index 4dbcc27..0000000
--- a/formats/cbor/build.gradle
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-apply plugin: 'kotlin-multiplatform'
-apply plugin: 'kotlinx-serialization'
-apply from: rootProject.file("gradle/native-targets.gradle")
-apply from: rootProject.file("gradle/configure-source-sets.gradle")
-
-kotlin {
-
-    sourceSets {
-        commonMain {
-            dependencies {
-                api project(":kotlinx-serialization-core")
-            }
-        }
-
-        jvmTest {
-            dependencies {
-                implementation 'io.kotlintest:kotlintest:2.0.7'
-                implementation 'com.upokecenter:cbor:4.2.0'
-                implementation "com.fasterxml.jackson.core:jackson-core:$jackson_version"
-                implementation "com.fasterxml.jackson.core:jackson-databind:$jackson_version"
-                implementation "com.fasterxml.jackson.module:jackson-module-kotlin:$jackson_version"
-                implementation "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:$jackson_version"
-                implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
-            }
-        }
-    }
-}
-
-Java9Modularity.configureJava9ModuleInfo(project)
diff --git a/formats/cbor/build.gradle.kts b/formats/cbor/build.gradle.kts
new file mode 100644
index 0000000..5b3c7ff
--- /dev/null
+++ b/formats/cbor/build.gradle.kts
@@ -0,0 +1,38 @@
+import Java9Modularity.configureJava9ModuleInfo
+
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+plugins {
+    kotlin("multiplatform")
+    alias(libs.plugins.serialization)
+
+    id("native-targets-conventions")
+    id("source-sets-conventions")
+}
+
+kotlin {
+
+    sourceSets {
+        commonMain {
+            dependencies {
+                api(project(":kotlinx-serialization-core"))
+            }
+        }
+
+        jvmTest {
+            dependencies {
+                implementation(libs.kotlintest)
+                implementation(libs.cbor)
+                implementation(libs.jackson.core)
+                implementation(libs.jackson.databind)
+                implementation(libs.jackson.module.kotlin)
+                implementation(libs.jackson.cbor)
+                implementation(libs.coroutines.core)
+            }
+        }
+    }
+}
+
+configureJava9ModuleInfo()
diff --git a/formats/cbor/commonMain/src/kotlinx/serialization/cbor/Cbor.kt b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/Cbor.kt
index 9e76a8f..21293a9 100644
--- a/formats/cbor/commonMain/src/kotlinx/serialization/cbor/Cbor.kt
+++ b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/Cbor.kt
@@ -5,9 +5,6 @@
 package kotlinx.serialization.cbor
 
 import kotlinx.serialization.*
-import kotlinx.serialization.builtins.*
-import kotlinx.serialization.cbor.internal.ByteArrayInput
-import kotlinx.serialization.cbor.internal.ByteArrayOutput
 import kotlinx.serialization.cbor.internal.*
 import kotlinx.serialization.modules.*
 
@@ -18,45 +15,90 @@
  * if necessary, registered custom serializers (in [SerializersModule] provided by [serializersModule] constructor parameter).
  *
  * ### Known caveats and limitations:
- * Supports reading collections of both definite and indefinite lengths; however,
- * serialization always writes maps and lists as [indefinite-length](https://tools.ietf.org/html/rfc7049#section-2.2.1) ones.
- * Does not support [optional tags](https://tools.ietf.org/html/rfc7049#section-2.4) representing datetime, bignums, etc.
+ * Can be used to produce fully [COSE](https://datatracker.ietf.org/doc/html/rfc8812)-compliant data
+ * but canonical sorting of map keys needs to be done manually by specifying members in appropriate order.
  * Fully support CBOR maps, which, unlike JSON ones, may contain keys of non-primitive types, and may produce such maps
  * from corresponding Kotlin objects. However, other 3rd-party parsers (e.g. `jackson-dataformat-cbor`) may not accept such maps.
- *
- * @param encodeDefaults specifies whether default values of Kotlin properties are encoded.
- *                       False by default; meaning that properties with values equal to defaults will be elided.
- * @param ignoreUnknownKeys specifies if unknown CBOR elements should be ignored (skipped) when decoding.
  */
 @ExperimentalSerializationApi
 public sealed class Cbor(
-    internal val encodeDefaults: Boolean,
-    internal val ignoreUnknownKeys: Boolean,
+    public val configuration: CborConfiguration,
     override val serializersModule: SerializersModule
 ) : BinaryFormat {
 
     /**
-     * The default instance of [Cbor]
+     * The default instance of [Cbor]. Neither writes nor verifies tags. Uses indefinite length encoding by default.
      */
-    public companion object Default : Cbor(false, false, EmptySerializersModule())
+    public companion object Default :
+        Cbor(
+            CborConfiguration(
+                encodeDefaults = false,
+                ignoreUnknownKeys = false,
+                encodeKeyTags = false,
+                encodeValueTags = false,
+                encodeObjectTags = false,
+                verifyKeyTags = false,
+                verifyValueTags = false,
+                verifyObjectTags = false,
+                useDefiniteLengthEncoding = false,
+                preferCborLabelsOverNames = false,
+                alwaysUseByteString = false
+            ), EmptySerializersModule()
+        ) {
+
+        /**
+         * Preconfigured instance of [Cbor] for COSE compliance. Encodes and verifies all tags, uses definite length
+         * encoding and prefers labels to serial names. **DOES NOT** sort CBOR map keys; declare them in canonical order
+         * for full cbor compliance!
+         */
+        public val CoseCompliant: Cbor =
+            Cbor {
+                encodeDefaults = false
+                ignoreUnknownKeys = false
+                encodeKeyTags = true
+                encodeValueTags = true
+                encodeObjectTags = true
+                verifyKeyTags = true
+                verifyValueTags = true
+                verifyObjectTags = true
+                useDefiniteLengthEncoding = true
+                preferCborLabelsOverNames = true
+                alwaysUseByteString = false
+                serializersModule = EmptySerializersModule()
+            }
+    }
 
     override fun <T> encodeToByteArray(serializer: SerializationStrategy<T>, value: T): ByteArray {
         val output = ByteArrayOutput()
-        val dumper = CborWriter(this, CborEncoder(output))
+        val dumper = if (configuration.useDefiniteLengthEncoding) DefiniteLengthCborWriter(
+            this,
+            output
+        ) else IndefiniteLengthCborWriter(
+            this,
+            output
+        )
         dumper.encodeSerializableValue(serializer, value)
+
         return output.toByteArray()
+
     }
 
     override fun <T> decodeFromByteArray(deserializer: DeserializationStrategy<T>, bytes: ByteArray): T {
         val stream = ByteArrayInput(bytes)
-        val reader = CborReader(this, CborDecoder(stream))
+        val reader = CborReader(this, CborParser(stream, configuration.verifyObjectTags))
         return reader.decodeSerializableValue(deserializer)
     }
 }
 
 @OptIn(ExperimentalSerializationApi::class)
-private class CborImpl(encodeDefaults: Boolean, ignoreUnknownKeys: Boolean, serializersModule: SerializersModule) :
-    Cbor(encodeDefaults, ignoreUnknownKeys, serializersModule)
+private class CborImpl(
+    configuration: CborConfiguration,
+    serializersModule: SerializersModule
+) :
+    Cbor(
+        configuration,
+        serializersModule
+    )
 
 /**
  * Creates an instance of [Cbor] configured from the optionally given [Cbor instance][from]
@@ -66,7 +108,20 @@
 public fun Cbor(from: Cbor = Cbor, builderAction: CborBuilder.() -> Unit): Cbor {
     val builder = CborBuilder(from)
     builder.builderAction()
-    return CborImpl(builder.encodeDefaults, builder.ignoreUnknownKeys, builder.serializersModule)
+    return CborImpl(CborConfiguration(
+        builder.encodeDefaults,
+        builder.ignoreUnknownKeys,
+        builder.encodeKeyTags,
+        builder.encodeValueTags,
+        builder.encodeObjectTags,
+        builder.verifyKeyTags,
+        builder.verifyValueTags,
+        builder.verifyObjectTags,
+        builder.useDefiniteLengthEncoding,
+        builder.preferCborLabelsOverNames,
+        builder.alwaysUseByteString),
+        builder.serializersModule
+    )
 }
 
 /**
@@ -78,14 +133,115 @@
     /**
      * Specifies whether default values of Kotlin properties should be encoded.
      */
-    public var encodeDefaults: Boolean = cbor.encodeDefaults
+    public var encodeDefaults: Boolean = cbor.configuration.encodeDefaults
 
     /**
      * Specifies whether encounters of unknown properties in the input CBOR
      * should be ignored instead of throwing [SerializationException].
      * `false` by default.
      */
-    public var ignoreUnknownKeys: Boolean = cbor.ignoreUnknownKeys
+    public var ignoreUnknownKeys: Boolean = cbor.configuration.ignoreUnknownKeys
+
+    /**
+     * Specifies whether tags set using the [KeyTags] annotation should be written.
+     * CBOR allows for optionally defining *tags* for properties and their values. When this switch is set to `true` tags on
+     * CBOR map keys (i.e. properties) are encoded into the resulting byte string to transport additional information.
+     * See [RFC 8949 Tagging of Items](https://datatracker.ietf.org/doc/html/rfc8949#name-tagging-of-items) for more info.
+     */
+    public var encodeKeyTags: Boolean = cbor.configuration.encodeKeyTags
+
+    /**
+     * Specifies whether tags set using the [ValueTags] annotation should be written.
+     * CBOR allows for optionally defining *tags* for properties and their values. When this switch is set to `true`, tags on
+     * CBOR map values (i.e. the values of properties and map entries) are encoded into the resulting byte string to
+     * transport additional information. Well-known tags are specified in [CborTag].
+     * See [RFC 8949 Tagging of Items](https://datatracker.ietf.org/doc/html/rfc8949#name-tagging-of-items) for more info.
+     */
+    public var encodeValueTags: Boolean = cbor.configuration.encodeValueTags
+
+    /**
+     * Specifies whether tags set using the [ObjectTags] annotation should be written.
+     * When this switch is set to `true` , it is possible to directly declare classes to always be tagged.
+     * This then applies to isolated objects of such a tagged class being serialized and to objects of such a class used as
+     * values in a list, but also or when they are used as a property in another class.
+     * Forcing objects to always be tagged in such a manner is accomplished by the [ObjectTags] annotation,
+     * which works just as [ValueTags], but for class definitions.
+     * When serializing, object tags will always be encoded directly before to the data of the tagged object, i.e. a
+     * value-tagged property of an object-tagged type will have the value tags preceding the object tags.
+     * Well-known tags are specified in [CborTag].
+     * See [RFC 8949 Tagging of Items](https://datatracker.ietf.org/doc/html/rfc8949#name-tagging-of-items) for more info.
+     */
+    public var encodeObjectTags: Boolean = cbor.configuration.encodeObjectTags
+
+    /**
+     * Specifies whether tags preceding map keys (i.e. properties) should be matched against the
+     * [KeyTags] annotation during the deserialization process.
+     * CBOR allows for optionally defining *tags* for properties and their values. When the [encodeKeyTags] switch is set to
+     * `true` tags on CBOR map keys (i.e. properties) are encoded into the resulting byte string to transport additional
+     * information. Setting [verifyKeyTags] to `true` forces strict verification of such tags during deserialization.
+     * I.e. tags must be present on all properties of a class annotated with [KeyTags] in the CBOR byte stream
+     * **in full and in order**.
+     * See [RFC 8949 Tagging of Items](https://datatracker.ietf.org/doc/html/rfc8949#name-tagging-of-items) for more info.
+     */
+    public var verifyKeyTags: Boolean = cbor.configuration.verifyKeyTags
+
+    /**
+     * Specifies whether tags preceding values should be matched against the [ValueTags]
+     * annotation during the deserialization process.
+     * CBOR allows for optionally defining *tags* for properties and their values. When [encodeValueTags] is set to `true`,
+     * tags on CBOR map values (i.e. the values of properties and map entries) are encoded into the resulting byte string to
+     * transport additional information.
+     * Setting [verifyValueTags] to `true` forces verification of such tags during deserialization. I.e. tags must be
+     * present on all values annotated with [ValueTags] in the CBOR byte stream **in full and in order**.
+     * See also [verifyObjectTags], since a value may have both kinds of tags. [ValueTags] precede [ObjectTags] in the CBOR
+     * byte stream. [verifyValueTags] and [verifyObjectTags] can be toggled independently.
+     * Well-known tags are specified in [CborTag].
+     */
+    public var verifyValueTags: Boolean = cbor.configuration.verifyValueTags
+
+    /**
+     * Specifies whether tags preceding values should be matched against the [ObjectTags]
+     * annotation during the deserialization process. [ObjectTags] are applied when serializing classes tagged using this
+     * annotation. This applies to isolated objects of such a class and properties, whose values are of such a tagged class.
+     * [verifyValueTags] and [verifyObjectTags] can be toggled independently. Hence, it is possible to only partially verify
+     * tags on values (if only one such configuration switch is set to true). [ValueTags] precede [ObjectTags] in the CBOR
+     * byte stream.
+     * Well-known tags are specified in [CborTag].
+     */
+    public var verifyObjectTags: Boolean = cbor.configuration.verifyObjectTags
+
+    /**
+     * Specifies whether the definite length encoding should be used (as required for COSE, for example).
+     * CBOR supports two encodings for maps and arrays: definite and indefinite length encoding. kotlinx.serialization defaults
+     * to the latter, which means that a map's or array's number of elements is not encoded, but instead a terminating byte is
+     * appended after the last element.
+     * Definite length encoding, on the other hand, omits this terminating byte, but instead prepends number of elements
+     * to the contents of a map or array. This configuration switch allows for toggling between the
+     * two modes of encoding.
+     */
+    public var useDefiniteLengthEncoding: Boolean = cbor.configuration.useDefiniteLengthEncoding
+
+    /**
+     * Specifies whether to serialize element labels (i.e. Long from [CborLabel])
+     * instead of the element names (i.e. String from [SerialName]). CBOR supports keys of all types which work just as
+     * `SerialName`s.
+     * COSE restricts this again to strings and numbers and calls these restricted map keys *labels*. String labels can be
+     * assigned by using `@SerialName`, while number labels can be assigned using the [CborLabel] annotation.
+     * The [preferCborLabelsOverNames] configuration switch can be used to prefer number labels over SerialNames in case both
+     * are present for a property. This duality allows for compact representation of a type when serialized to CBOR, while
+     * keeping expressive diagnostic names when serializing to JSON.
+     */
+    public var preferCborLabelsOverNames: Boolean = cbor.configuration.preferCborLabelsOverNames
+
+    /**
+     * Specifies whether to always use the compact [ByteString] encoding when serializing
+     * or deserializing byte arrays.
+     * By default, Kotlin `ByteArray` instances are encoded as **major type 4**.
+     * When **major type 2** is desired, then the [`@ByteString`][ByteString] annotation can be used on a case-by-case
+     * basis. The [alwaysUseByteString] configuration switch allows for globally preferring **major type 2** without needing
+     * to annotate every `ByteArray` in a class hierarchy.
+     */
+    public var alwaysUseByteString: Boolean = cbor.configuration.alwaysUseByteString
 
     /**
      * Module with contextual and polymorphic serializers to be used in the resulting [Cbor] instance.
diff --git a/formats/cbor/commonMain/src/kotlinx/serialization/cbor/CborArray.kt b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/CborArray.kt
new file mode 100644
index 0000000..9727b60
--- /dev/null
+++ b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/CborArray.kt
@@ -0,0 +1,39 @@
+package kotlinx.serialization.cbor
+
+import kotlinx.serialization.*
+
+/**
+ * Encode a class as a CBOR Array (Major type 4) instead of a CBOR map.
+ *
+ * Serialization of such a class will skip element names (or labels),
+ * only encoding the values (containing explicit nulls where necessary).
+ *
+ * Example usage:
+ *
+ * ```
+ * @CborArray
+ * @Serializable
+ * data class DataClass(
+ *     val alg: Int,
+ *     val kid: String?
+ * )
+ *
+ * Cbor.encodeToByteArray(DataClass(alg = -7, kid = null))
+ * ```
+ *
+ * will produce bytes `0x8226F6`, or in diagnostic notation:
+ *
+ * ```
+ * 82    # array(2)
+ *    26 # negative(6)
+ *    F6 # primitive(22)
+ * ```
+ *
+ * This may be used to encode COSE structures, see
+ * [RFC 9052 2. Basic COSE Structure](https://www.rfc-editor.org/rfc/rfc9052#section-2).
+ *
+ */
+@SerialInfo
+@Target(AnnotationTarget.CLASS)
+@ExperimentalSerializationApi
+public annotation class CborArray
diff --git a/formats/cbor/commonMain/src/kotlinx/serialization/cbor/CborConfiguration.kt b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/CborConfiguration.kt
new file mode 100644
index 0000000..3d88627
--- /dev/null
+++ b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/CborConfiguration.kt
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.cbor
+
+import kotlinx.serialization.*
+
+/**
+ * Configuration of the current [Cbor] instance available through [Cbor.configuration].
+ *
+ *  * Can be used for debug purposes and for custom Cbor-specific serializers
+ *  * via [CborEncoder] and [CborDecoder].
+ *
+ * @param encodeDefaults specifies whether default values of Kotlin properties are encoded.
+ * False by default; meaning that properties with values equal to defaults will be elided.
+ * @param ignoreUnknownKeys specifies if unknown CBOR elements should be ignored (skipped) when decoding.
+ * @param encodeKeyTags Specifies whether tags set using the [KeyTags] annotation should be written.
+ * CBOR allows for optionally defining *tags* for properties and their values. When this switch is set to `true` tags on
+ * CBOR map keys (i.e. properties) are encoded into the resulting byte string to transport additional information.
+ * See [RFC 8949 Tagging of Items](https://datatracker.ietf.org/doc/html/rfc8949#name-tagging-of-items) for more info.
+ *
+ * @param encodeValueTags Specifies whether tags set using the [ValueTags] annotation should be written.
+ * CBOR allows for optionally defining *tags* for properties and their values. When this switch is set to `true`, tags on
+ * CBOR map values (i.e. the values of properties and map entries) are encoded into the resulting byte string to
+ * transport additional information. Well-known tags are specified in [CborTag].
+ * See [RFC 8949 Tagging of Items](https://datatracker.ietf.org/doc/html/rfc8949#name-tagging-of-items) for more info.
+ *
+ * @param encodeObjectTags Specifies whether tags set using the [ObjectTags] annotation should be written.
+ * When this switch is set to `true` , it is possible to directly declare classes to always be tagged.
+ * This then applies to isolated objects of such a tagged class being serialized and to objects of such a class used as
+ * values in a list, but also or when they are used as a property in another class.
+ * Forcing objects to always be tagged in such a manner is accomplished by the [ObjectTags] annotation,
+ * which works just as [ValueTags], but for class definitions.
+ * When serializing, object tags will always be encoded directly before to the data of the tagged object, i.e. a
+ * value-tagged property of an object-tagged type will have the value tags preceding the object tags.
+ * Well-known tags are specified in [CborTag].
+ * See [RFC 8949 Tagging of Items](https://datatracker.ietf.org/doc/html/rfc8949#name-tagging-of-items) for more info.
+
+ * @param verifyKeyTags Specifies whether tags preceding map keys (i.e. properties) should be matched against the
+ * [KeyTags] annotation during the deserialization process.
+ * CBOR allows for optionally defining *tags* for properties and their values. When the [encodeKeyTags] switch is set to
+ * `true` tags on CBOR map keys (i.e. properties) are encoded into the resulting byte string to transport additional
+ * information. Setting [verifyKeyTags] to `true` forces strict verification of such tags during deserialization.
+ * I.e. tags must be present on all properties of a class annotated with [KeyTags] in the CBOR byte stream
+ * **in full and in order**.
+ * See [RFC 8949 Tagging of Items](https://datatracker.ietf.org/doc/html/rfc8949#name-tagging-of-items) for more info.
+ *
+ * @param verifyValueTags Specifies whether tags preceding values should be matched against the [ValueTags]
+ * annotation during the deserialization process.
+ * CBOR allows for optionally defining *tags* for properties and their values. When [encodeValueTags] is set to `true`,
+ * tags on CBOR map values (i.e. the values of properties and map entries) are encoded into the resulting byte string to
+ * transport additional information.
+ * Setting [verifyValueTags] to `true` forces verification of such tags during deserialization. I.e. tags must be
+ * present on all values annotated with [ValueTags] in the CBOR byte stream **in full and in order**.
+ * See also [verifyObjectTags], since a value may have both kinds of tags. [ValueTags] precede [ObjectTags] in the CBOR
+ * byte stream. [verifyValueTags] and [verifyObjectTags] can be toggled independently.
+ * Well-known tags are specified in [CborTag].
+ *
+ * @param verifyObjectTags Specifies whether tags preceding values should be matched against the [ObjectTags]
+ * annotation during the deserialization process. [ObjectTags] are applied when serializing classes tagged using this
+ * annotation. This applies to isolated objects of such a class and properties, whose values are of such a tagged class.
+ * [verifyValueTags] and [verifyObjectTags] can be toggled independently. Hence, it is possible to only partially verify
+ * tags on values (if only one such configuration switch is set to true). [ValueTags] precede [ObjectTags] in the CBOR
+ * byte stream.
+ * Well-known tags are specified in [CborTag].
+ *
+ * @param useDefiniteLengthEncoding Specifies whether the definite length encoding should be used (as required for COSE, for example).
+ * CBOR supports two encodings for maps and arrays: definite and indefinite length encoding. kotlinx.serialization defaults
+ * to the latter, which means that a map's or array's number of elements is not encoded, but instead a terminating byte is
+ * appended after the last element.
+ * Definite length encoding, on the other hand, omits this terminating byte, but instead prepends number of elements
+ * to the contents of a map or array. This configuration switch allows for toggling between the
+ * two modes of encoding.
+ *
+ * @param preferCborLabelsOverNames Specifies whether to serialize element labels (i.e. Long from [CborLabel])
+ * instead of the element names (i.e. String from [SerialName]). CBOR supports keys of all types which work just as
+ * `SerialName`s.
+ * COSE restricts this again to strings and numbers and calls these restricted map keys *labels*. String labels can be
+ * assigned by using `@SerialName`, while number labels can be assigned using the [CborLabel] annotation.
+ * The [preferCborLabelsOverNames] configuration switch can be used to prefer number labels over SerialNames in case both
+ * are present for a property. This duality allows for compact representation of a type when serialized to CBOR, while
+ * keeping expressive diagnostic names when serializing to JSON.
+ *
+ * @param alwaysUseByteString Specifies whether to always use the compact [ByteString] encoding when serializing
+ * or deserializing byte arrays.
+ * By default, Kotlin `ByteArray` instances are encoded as **major type 4**.
+ * When **major type 2** is desired, then the [`@ByteString`][ByteString] annotation can be used on a case-by-case
+ * basis. The [alwaysUseByteString] configuration switch allows for globally preferring **major type 2** without needing
+ * to annotate every `ByteArray` in a class hierarchy.
+ *
+ */
+@ExperimentalSerializationApi
+public class CborConfiguration internal constructor(
+    public val encodeDefaults: Boolean,
+    public val ignoreUnknownKeys: Boolean,
+    public val encodeKeyTags: Boolean,
+    public val encodeValueTags: Boolean,
+    public val encodeObjectTags: Boolean,
+    public val verifyKeyTags: Boolean,
+    public val verifyValueTags: Boolean,
+    public val verifyObjectTags: Boolean,
+    public val useDefiniteLengthEncoding: Boolean,
+    public val preferCborLabelsOverNames: Boolean,
+    public val alwaysUseByteString: Boolean,
+) {
+    override fun toString(): String {
+        return "CborConfiguration(encodeDefaults=$encodeDefaults, ignoreUnknownKeys=$ignoreUnknownKeys, " +
+            "encodeKeyTags=$encodeKeyTags, encodeValueTags=$encodeValueTags, encodeObjectTags=$encodeObjectTags, " +
+            "verifyKeyTags=$verifyKeyTags, verifyValueTags=$verifyValueTags, verifyObjectTags=$verifyObjectTags, " +
+            "useDefiniteLengthEncoding=$useDefiniteLengthEncoding, " +
+            "preferCborLabelsOverNames=$preferCborLabelsOverNames, alwaysUseByteString=$alwaysUseByteString)"
+    }
+}
\ No newline at end of file
diff --git a/formats/cbor/commonMain/src/kotlinx/serialization/cbor/CborDecoder.kt b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/CborDecoder.kt
new file mode 100644
index 0000000..c30c765
--- /dev/null
+++ b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/CborDecoder.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.cbor
+
+import kotlinx.serialization.*
+import kotlinx.serialization.encoding.*
+
+/**
+ * This interface provides access to the current Cbor instance, so it can be properly taken into account in a
+ * custom serializer. For example, a custom serializer can decode CBOR data wrapped into a byte array using
+ * [Cbor.decodeFromByteArray] as required by some COSE structures.
+ * The actual CBOR Decoder used during deserialization implements this interface, so it is possible to cast the decoder
+ * passed to [KSerializer.deserialize] to [CborDecoder] when implementing such low-level serializers,
+ * to access configuration properties:
+ *
+ * ```kotlin
+ * override fun deserialize(decoder: Decoder): AlgorithmParameters {
+ *   if(decoder is CborDecoder){
+ *     val useDefiniteLengthEncoding = (decoder as CborDecoder).cbor.configuration.writeDefiniteLengths
+ *     // Do CBOR-specific low-level stuff
+ *   }
+ * }
+ * ```
+ */
+@ExperimentalSerializationApi
+public interface CborDecoder : Decoder {
+    /**
+     * Exposes the current [Cbor] instance and all its configuration flags. Useful for low-level custom serializers.
+     */
+    public val cbor: Cbor
+}
\ No newline at end of file
diff --git a/formats/cbor/commonMain/src/kotlinx/serialization/cbor/CborEncoder.kt b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/CborEncoder.kt
new file mode 100644
index 0000000..929a753
--- /dev/null
+++ b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/CborEncoder.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.cbor
+
+import kotlinx.serialization.*
+import kotlinx.serialization.encoding.*
+
+/**
+ * This interface provides access to the current Cbor instance, so it can be properly taken into account in a
+ * custom serializer. For example, a custom serializer can output a byte array using [Cbor.encodeToByteArray]
+ * and embed resulting data into the output, as required, by some COSE structures.
+ * The actual CBOR Encoder used during serialization implements this interface, so it is possible to cast the encoder
+ * passed to [KSerializer.serialize] to [CborEncoder] when implementing such low-level serializers,
+ * to access configuration properties:
+ *
+ * ```kotlin
+ * override fun serialize(encoder: Encoder, value: AlgorithmParameters) {
+ *   if (encoder is CborEncoder) {
+ *     val useDefiniteLengthEncoding = (encoder as CborEncoder).cbor.configuration.writeDefiniteLengths
+ *     // Do CBOR-specific low-level stuff
+ *     }
+ * }
+ * ```
+ */
+@ExperimentalSerializationApi
+public interface CborEncoder : Encoder {
+    /**
+     * Exposes the current [Cbor] instance and all its configuration flags. Useful for low-level custom serializers.
+     */
+    public val cbor: Cbor
+}
\ No newline at end of file
diff --git a/formats/cbor/commonMain/src/kotlinx/serialization/cbor/CborLabel.kt b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/CborLabel.kt
new file mode 100644
index 0000000..7d6255e
--- /dev/null
+++ b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/CborLabel.kt
@@ -0,0 +1,48 @@
+package kotlinx.serialization.cbor
+
+import kotlinx.serialization.*
+
+/**
+ * CBOR supports keys of all sorts, not just Strings.
+ * In the COSE context, these keys are called *labels* and are limited to Strings and 64-bit negative integers
+ * and 64-bit unsigned integers.
+ * Conceptually, these work just as `SerialName`s, but to also support numbers in addition to Strings, this annotation
+ * can be used.
+ *
+ * Set the `preferCborLabelsOverNames` configuration switch to prefer them over serial names in case both are present
+ * for a property.
+ *
+ * Example usage:
+ * ```
+ * @Serializable
+ * data class DataClass(
+ *     @CborLabel(1)
+ *     @SerialName("alg")
+ *     val alg: Int
+ * )
+ * ```
+ *
+ * serializing `DataClass(alg = -7)` with `Cbor { preferCborLabelsOverNames = true }` will
+ * output `0xbf0126ff`, or in diagnostic notation:
+ *
+ * ```
+ * BF    # map(*)
+ *    01 # unsigned(1)
+ *    26 # negative(6)
+ *    FF # primitive(*)
+ * ```
+ *
+ * instead of the traditional `0xbf63616c6726ff`, or in diagnostic notation:
+ *
+ * ```
+ * BF           # map(*)
+ *    63        # text(3)
+ *       616C67 # "alg"
+ *    26        # negative(6)
+ *    FF        # primitive(*)
+ * ```
+ */
+@SerialInfo
+@Target(AnnotationTarget.PROPERTY)
+@ExperimentalSerializationApi
+public annotation class CborLabel(val label: Long)
\ No newline at end of file
diff --git a/formats/cbor/commonMain/src/kotlinx/serialization/cbor/Tags.kt b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/Tags.kt
new file mode 100644
index 0000000..2d32058
--- /dev/null
+++ b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/Tags.kt
@@ -0,0 +1,113 @@
+package kotlinx.serialization.cbor
+
+import kotlinx.serialization.cbor.internal.SuppressAnimalSniffer
+import kotlinx.serialization.*
+
+/**
+ * Specifies that a property shall be tagged and the tag is serialized as CBOR major type 6: optional semantic tagging
+ * of other major types.
+ *
+ * Example usage:
+ *
+ * ```
+ * @Serializable
+ * data class Data(
+ *     @ValueTags(1337uL)
+ *     @ByteString
+ *     val a: ByteArray, // CBOR major type 6 1337(major type 2: a byte string).
+ *
+ *     @ValueTags(1234567uL)
+ *     val b: ByteArray  // CBOR major type 6 1234567(major type 4: an array of data items).
+ * )
+ * ```
+ *
+ * See [RFC 8949 3.4. Tagging of Items](https://datatracker.ietf.org/doc/html/rfc8949#name-tagging-of-items).
+ */
+@SerialInfo
+@Target(AnnotationTarget.PROPERTY)
+@ExperimentalSerializationApi
+@SuppressAnimalSniffer
+public annotation class ValueTags(@OptIn(ExperimentalUnsignedTypes::class) vararg val tags: ULong)
+
+/**
+ * Contains a set of predefined tags, named in accordance with
+ * [RFC 8949 3.4. Tagging of Items](https://datatracker.ietf.org/doc/html/rfc8949#name-tagging-of-items)
+ */
+public object CborTag{
+    public const val DATE_TIME_STANDARD: ULong = 0u;
+    public const val DATE_TIME_EPOCH: ULong = 1u;
+    public const val BIGNUM_POSITIVE: ULong = 2u;
+    public const val BIGNUM_NEGAIVE: ULong = 3u;
+    public const val DECIMAL_FRACTION: ULong = 4u;
+    public const val BIGFLOAT: ULong = 5u;
+    public const val BASE64_URL: ULong = 21u;
+    public const val BASE64: ULong = 22u;
+    public const val BASE16: ULong = 23u;
+    public const val CBOR_ENCODED_DATA: ULong = 24u;
+    public const val URI: ULong = 32u;
+    public const val STRING_BASE64_URL: ULong = 33u;
+    public const val STRING_BASE64: ULong = 34u;
+    public const val REGEX: ULong = 35u;
+    public const val MIME_MESSAGE: ULong = 36u;
+    public const val CBOR_SELF_DESCRIBE: ULong = 55799u;
+}
+
+/**
+ * Specifies that a key (i.e. a property identifier) shall be tagged and serialized as CBOR major type 6: optional
+ * semantic tagging of other major types.
+ *
+ * Example usage:
+ *
+ * ```
+ * @Serializable
+ * data class Data(
+ *     @KeyTags(34uL)
+ *     val b: Int = -1   // results in the CBOR equivalent of 34("b"): -1
+ * )
+ * ```
+ *
+ * See [RFC 8949 3.4. Tagging of Items](https://datatracker.ietf.org/doc/html/rfc8949#name-tagging-of-items).
+ */
+@SerialInfo
+@Target(AnnotationTarget.PROPERTY)
+@ExperimentalSerializationApi
+@SuppressAnimalSniffer
+public annotation class KeyTags(@OptIn(ExperimentalUnsignedTypes::class) vararg val tags: ULong)
+
+
+
+/**
+ * Specifies that an object of a class annotated using `ObjectTags` shall be tagged and serialized as
+ * CBOR major type 6: optional semantic tagging of other major types. Can be combined with [CborArray] and [ValueTags].
+ * Note that `ObjectTags` will always be encoded directly before to the data of the tagged object, i.e. a value-tagged
+ * property of an object-tagged type will have the value tags preceding the object tags.
+ *
+ * Example usage:
+ *
+ * ```
+ * @ObjectTags(1337uL)
+ * @Serializable
+ * data class ClassAsTagged(
+ *     @SerialName("alg")
+ *     val alg: Int,
+ * )
+ * ```
+ *
+ * Encoding to CBOR results in the following byte string:
+ * ```
+ * D9 0539         # tag(1337)
+ *    BF           # map(*)
+ *       63        # text(3)
+ *          616C67 # "alg"
+ *       13        # unsigned(19)
+ *       FF        # primitive(*)
+ * ```
+ *
+ * See [RFC 8949 3.4. Tagging of Items](https://datatracker.ietf.org/doc/html/rfc8949#name-tagging-of-items).
+ */
+@SerialInfo
+@Target(AnnotationTarget.CLASS)
+@ExperimentalSerializationApi
+@SuppressAnimalSniffer
+public annotation class ObjectTags(@OptIn(ExperimentalUnsignedTypes::class) vararg val tags: ULong)
+
diff --git a/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/Decoder.kt b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/Decoder.kt
new file mode 100644
index 0000000..174f8fc
--- /dev/null
+++ b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/Decoder.kt
@@ -0,0 +1,618 @@
+/*
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+@file:OptIn(ExperimentalSerializationApi::class, ExperimentalUnsignedTypes::class)
+
+package kotlinx.serialization.cbor.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.cbor.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.modules.*
+
+internal open class CborReader(override val cbor: Cbor, protected val parser: CborParser) : AbstractDecoder(),
+    CborDecoder {
+
+    protected var size = -1
+        private set
+    protected var finiteMode = false
+        private set
+    private var readProperties: Int = 0
+
+    protected var decodeByteArrayAsByteString = false
+    protected var tags: ULongArray? = null
+
+    protected fun setSize(size: Int) {
+        if (size >= 0) {
+            finiteMode = true
+            this.size = size
+        }
+    }
+
+    override val serializersModule: SerializersModule
+        get() = cbor.serializersModule
+
+    protected open fun skipBeginToken(objectTags: ULongArray?) = setSize(parser.startMap(objectTags))
+
+    @OptIn(ExperimentalSerializationApi::class)
+    override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
+        val re = if (descriptor.hasArrayTag()) {
+            CborListReader(cbor, parser)
+        } else when (descriptor.kind) {
+            StructureKind.LIST, is PolymorphicKind -> CborListReader(cbor, parser)
+            StructureKind.MAP -> CborMapReader(cbor, parser)
+            else -> CborReader(cbor, parser)
+        }
+        val objectTags = if (cbor.configuration.verifyObjectTags) descriptor.getObjectTags() else null
+        re.skipBeginToken(tags?.let { if (objectTags == null) it else ulongArrayOf(*it, *objectTags) } ?: objectTags)
+        return re
+    }
+
+    override fun endStructure(descriptor: SerialDescriptor) {
+        if (!finiteMode) parser.end()
+    }
+
+    override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+        val index = if (cbor.configuration.ignoreUnknownKeys) {
+            val knownIndex: Int
+            while (true) {
+                if (isDone()) return CompositeDecoder.DECODE_DONE
+                val (elemName, tags) = decodeElementNameWithTagsLenient(descriptor)
+                readProperties++
+
+                val index = elemName?.let { descriptor.getElementIndex(it) } ?: CompositeDecoder.UNKNOWN_NAME
+                if (index == CompositeDecoder.UNKNOWN_NAME) {
+                    parser.skipElement(tags)
+                } else {
+                    verifyKeyTags(descriptor, index, tags)
+                    knownIndex = index
+                    break
+                }
+            }
+            knownIndex
+        } else {
+            if (isDone()) return CompositeDecoder.DECODE_DONE
+            val (elemName, tags) = decodeElementNameWithTags(descriptor)
+            readProperties++
+            descriptor.getElementIndexOrThrow(elemName).also { index ->
+                verifyKeyTags(descriptor, index, tags)
+            }
+        }
+
+        decodeByteArrayAsByteString = descriptor.isByteString(index)
+        tags = if (cbor.configuration.verifyValueTags) descriptor.getValueTags(index) else null
+        return index
+    }
+
+
+    private fun decodeElementNameWithTags(descriptor: SerialDescriptor): Pair<String, ULongArray?> {
+        var (elemName, cborLabel, tags) = parser.nextTaggedStringOrNumber()
+        if (elemName == null && cborLabel != null) {
+            elemName = descriptor.getElementNameForCborLabel(cborLabel)
+                ?: throw CborDecodingException("CborLabel unknown: $cborLabel for $descriptor")
+        }
+        if (elemName == null) {
+            throw CborDecodingException("Expected (tagged) string or number, got nothing for $descriptor")
+        }
+        return elemName to tags
+    }
+
+    private fun decodeElementNameWithTagsLenient(descriptor: SerialDescriptor): Pair<String?, ULongArray?> {
+        var (elemName, cborLabel, tags) = parser.nextTaggedStringOrNumber()
+        if (elemName == null && cborLabel != null) {
+            elemName = descriptor.getElementNameForCborLabel(cborLabel)
+        }
+        return elemName to tags
+    }
+
+    @OptIn(ExperimentalSerializationApi::class)
+    override fun <T> decodeSerializableValue(deserializer: DeserializationStrategy<T>): T {
+        return if ((decodeByteArrayAsByteString || cbor.configuration.alwaysUseByteString)
+            && deserializer.descriptor == ByteArraySerializer().descriptor
+        ) {
+            @Suppress("UNCHECKED_CAST")
+            parser.nextByteString(tags) as T
+        } else {
+            decodeByteArrayAsByteString = decodeByteArrayAsByteString || deserializer.descriptor.isInlineByteString()
+            super<AbstractDecoder>.decodeSerializableValue(deserializer)
+        }
+    }
+
+    override fun decodeString() = parser.nextString(tags)
+
+    override fun decodeNotNullMark(): Boolean = !parser.isNull()
+
+    override fun decodeDouble() = parser.nextDouble(tags)
+    override fun decodeFloat() = parser.nextFloat(tags)
+
+    override fun decodeBoolean() = parser.nextBoolean(tags)
+
+    override fun decodeByte() = parser.nextNumber(tags).toByte()
+    override fun decodeShort() = parser.nextNumber(tags).toShort()
+    override fun decodeChar() = parser.nextNumber(tags).toInt().toChar()
+    override fun decodeInt() = parser.nextNumber(tags).toInt()
+    override fun decodeLong() = parser.nextNumber(tags)
+
+    override fun decodeNull() = parser.nextNull(tags)
+
+    override fun decodeEnum(enumDescriptor: SerialDescriptor): Int =
+        enumDescriptor.getElementIndexOrThrow(parser.nextString(tags))
+
+    private fun isDone(): Boolean = !finiteMode && parser.isEnd() || (finiteMode && readProperties >= size)
+
+    private fun verifyKeyTags(descriptor: SerialDescriptor, index: Int, tags: ULongArray?) {
+        if (cbor.configuration.verifyKeyTags) {
+            descriptor.getKeyTags(index)?.let { keyTags ->
+                parser.verifyTagsAndThrow(keyTags, tags)
+            }
+        }
+    }
+}
+
+internal class CborParser(private val input: ByteArrayInput, private val verifyObjectTags: Boolean) {
+    private var curByte: Int = -1
+
+    init {
+        readByte()
+    }
+
+    private fun readByte(): Int {
+        curByte = input.read()
+        return curByte
+    }
+
+    fun isEof() = curByte == -1
+
+    private fun skipByte(expected: Int) {
+        if (curByte != expected) throw CborDecodingException("byte ${printByte(expected)}", curByte)
+        readByte()
+    }
+
+    fun isNull() = (curByte == NULL || curByte == EMPTY_MAP)
+
+    fun nextNull(tags: ULongArray? = null): Nothing? {
+        processTags(tags)
+        if (curByte == NULL) {
+            skipByte(NULL)
+        } else if (curByte == EMPTY_MAP) {
+            skipByte(EMPTY_MAP)
+        }
+        return null
+    }
+
+    fun nextBoolean(tags: ULongArray? = null): Boolean {
+        processTags(tags)
+        val ans = when (curByte) {
+            TRUE -> true
+            FALSE -> false
+            else -> throw CborDecodingException("boolean value", curByte)
+        }
+        readByte()
+        return ans
+    }
+
+    fun startArray(tags: ULongArray? = null) = startSized(tags, BEGIN_ARRAY, HEADER_ARRAY, "array")
+
+    fun startMap(tags: ULongArray? = null) = startSized(tags, BEGIN_MAP, HEADER_MAP, "map")
+
+    private fun startSized(
+        tags: ULongArray?,
+        unboundedHeader: Int,
+        boundedHeaderMask: Int,
+        collectionType: String
+    ): Int {
+        processTags(tags)
+        if (curByte == unboundedHeader) {
+            skipByte(unboundedHeader)
+            return -1
+        }
+        if ((curByte and 0b111_00000) != boundedHeaderMask)
+            throw CborDecodingException("start of $collectionType", curByte)
+        val size = readNumber().toInt()
+        readByte()
+        return size
+    }
+
+    fun isEnd() = curByte == BREAK
+
+    fun end() = skipByte(BREAK)
+
+    fun nextByteString(tags: ULongArray? = null): ByteArray {
+        processTags(tags)
+        if ((curByte and 0b111_00000) != HEADER_BYTE_STRING)
+            throw CborDecodingException("start of byte string", curByte)
+        val arr = readBytes()
+        readByte()
+        return arr
+    }
+
+    fun nextString(tags: ULongArray? = null) = nextTaggedString(tags).first
+
+    //used for reading the tag names and names of tagged keys (of maps, and serialized classes)
+    private fun nextTaggedString(tags: ULongArray?): Pair<String, ULongArray?> {
+        val collectedTags = processTags(tags)
+        if ((curByte and 0b111_00000) != HEADER_STRING)
+            throw CborDecodingException("start of string", curByte)
+        val arr = readBytes()
+        val ans = arr.decodeToString()
+        readByte()
+        return ans to collectedTags
+    }
+
+    private fun readBytes(): ByteArray =
+        if (curByte and 0b000_11111 == ADDITIONAL_INFORMATION_INDEFINITE_LENGTH) {
+            readByte()
+            readIndefiniteLengthBytes()
+        } else {
+            val strLen = readNumber().toInt()
+            input.readExactNBytes(strLen)
+        }
+
+    private fun processTags(tags: ULongArray?): ULongArray? {
+        var index = 0
+        val collectedTags = mutableListOf<ULong>()
+        while ((curByte and 0b111_00000) == HEADER_TAG) {
+            val readTag = readNumber().toULong() // This is the tag number
+            collectedTags += readTag
+            // value tags and object tags are intermingled (keyTags are always separate)
+            // so this check only holds if we verify both
+            if (verifyObjectTags) {
+                tags?.let {
+                    if (index++ >= it.size) throw CborDecodingException("More tags found than the ${it.size} tags specified")
+                }
+            }
+            readByte()
+        }
+        return (if (collectedTags.isEmpty()) null else collectedTags.toULongArray()).also { collected ->
+            //We only want to compare if tags are actually set, otherwise, we don't care
+            tags?.let {
+                if (verifyObjectTags) { //again, this check only works if we verify value tags and object tags
+                    verifyTagsAndThrow(it, collected)
+                } else {
+                    // If we don't care for object tags, the best we can do is assure that the collected tags start with
+                    // the expected tags. (yes this could co somewhere else, but putting it here groups the code nicely
+                    // into if-else branches.
+                    if ((collectedTags.size < it.size)
+                        || (collectedTags.subList(0, it.size) != it.asList())
+                    ) throw CborDecodingException("CBOR tags $collectedTags do not start with specified tags $it")
+                }
+            }
+        }
+    }
+
+    internal fun verifyTagsAndThrow(expected: ULongArray, actual: ULongArray?) {
+        if (!expected.contentEquals(actual))
+            throw CborDecodingException(
+                "CBOR tags ${actual?.contentToString()} do not match expected tags ${expected.contentToString()}"
+            )
+    }
+
+    /**
+     * Used for reading the tags and either string (element name) or number (serial label)
+     */
+    fun nextTaggedStringOrNumber(): Triple<String?, Long?, ULongArray?> {
+        val collectedTags = processTags(null)
+        if ((curByte and 0b111_00000) == HEADER_STRING) {
+            val arr = readBytes()
+            val ans = arr.decodeToString()
+            readByte()
+            return Triple(ans, null, collectedTags)
+        } else {
+            val res = readNumber()
+            readByte()
+            return Triple(null, res, collectedTags)
+        }
+    }
+
+    fun nextNumber(tags: ULongArray? = null): Long {
+        processTags(tags)
+        val res = readNumber()
+        readByte()
+        return res
+    }
+
+    private fun readNumber(): Long {
+        val value = curByte and 0b000_11111
+        val negative = (curByte and 0b111_00000) == HEADER_NEGATIVE.toInt()
+        val bytesToRead = when (value) {
+            24 -> 1
+            25 -> 2
+            26 -> 4
+            27 -> 8
+            else -> 0
+        }
+        if (bytesToRead == 0) {
+            return if (negative) -(value + 1).toLong()
+            else value.toLong()
+        }
+        val res = input.readExact(bytesToRead)
+        return if (negative) -(res + 1)
+        else res
+    }
+
+    private fun ByteArrayInput.readExact(bytes: Int): Long {
+        val arr = readExactNBytes(bytes)
+        var result = 0L
+        for (i in 0 until bytes) {
+            result = (result shl 8) or (arr[i].toInt() and 0xFF).toLong()
+        }
+        return result
+    }
+
+    private fun ByteArrayInput.readExactNBytes(bytesCount: Int): ByteArray {
+        if (bytesCount > availableBytes) {
+            error("Unexpected EOF, available $availableBytes bytes, requested: $bytesCount")
+        }
+        val array = ByteArray(bytesCount)
+        read(array, 0, bytesCount)
+        return array
+    }
+
+    fun nextFloat(tags: ULongArray? = null): Float {
+        processTags(tags)
+        val res = when (curByte) {
+            NEXT_FLOAT -> Float.fromBits(readInt())
+            NEXT_HALF -> floatFromHalfBits(readShort())
+            else -> throw CborDecodingException("float header", curByte)
+        }
+        readByte()
+        return res
+    }
+
+    fun nextDouble(tags: ULongArray? = null): Double {
+        processTags(tags)
+        val res = when (curByte) {
+            NEXT_DOUBLE -> Double.fromBits(readLong())
+            NEXT_FLOAT -> Float.fromBits(readInt()).toDouble()
+            NEXT_HALF -> floatFromHalfBits(readShort()).toDouble()
+            else -> throw CborDecodingException("double header", curByte)
+        }
+        readByte()
+        return res
+    }
+
+    private fun readLong(): Long {
+        var result = 0L
+        for (i in 0..7) {
+            val byte = input.read()
+            result = (result shl 8) or byte.toLong()
+        }
+        return result
+    }
+
+    private fun readShort(): Short {
+        val highByte = input.read()
+        val lowByte = input.read()
+        return (highByte shl 8 or lowByte).toShort()
+    }
+
+    private fun readInt(): Int {
+        var result = 0
+        for (i in 0..3) {
+            val byte = input.read()
+            result = (result shl 8) or byte
+        }
+        return result
+    }
+
+    /**
+     * Skips the current value element. Bytes are processed to determine the element type (and corresponding length), to
+     * determine how many bytes to skip.
+     *
+     * For primitive (finite length) elements (e.g. unsigned integer, text string), their length is read and
+     * corresponding number of bytes are skipped.
+     *
+     * For elements that contain children (e.g. array, map), the child count is read and added to a "length stack"
+     * (which represents the "number of elements" at each depth of the CBOR data structure). When a child element has
+     * been skipped, the "length stack" is [pruned][prune]. For indefinite length elements, a special marker is added to
+     * the "length stack" which is only popped from the "length stack" when a CBOR [break][isEnd] is encountered.
+     */
+    fun skipElement(tags: ULongArray?) {
+        val lengthStack = mutableListOf<Int>()
+
+        processTags(tags)
+
+        do {
+            if (isEof()) throw CborDecodingException("Unexpected EOF while skipping element")
+
+            if (isIndefinite()) {
+                lengthStack.add(LENGTH_STACK_INDEFINITE)
+            } else if (isEnd()) {
+                if (lengthStack.removeLastOrNull() != LENGTH_STACK_INDEFINITE)
+                    throw CborDecodingException("next data item", curByte)
+                prune(lengthStack)
+            } else {
+                val header = curByte and 0b111_00000
+                val length = elementLength()
+                if (header == HEADER_ARRAY || header == HEADER_MAP) {
+                    if (length > 0) lengthStack.add(length)
+                    processTags(tags)
+                } else {
+                    input.skip(length)
+                    prune(lengthStack)
+                }
+            }
+
+            readByte()
+        } while (lengthStack.isNotEmpty())
+    }
+
+    /**
+     * Removes an item from the top of the [lengthStack], cascading the removal if the item represents the last item
+     * (i.e. a length value of `1`) at its stack depth.
+     *
+     * For example, pruning a [lengthStack] of `[3, 2, 1, 1]` would result in `[3, 1]`.
+     */
+    private fun prune(lengthStack: MutableList<Int>) {
+        for (i in lengthStack.lastIndex downTo 0) {
+            when (lengthStack[i]) {
+                LENGTH_STACK_INDEFINITE -> break
+                1 -> lengthStack.removeAt(i)
+                else -> {
+                    lengthStack[i] = lengthStack[i] - 1
+                    break
+                }
+            }
+        }
+    }
+
+    /**
+     * Determines if [curByte] represents an indefinite length CBOR item.
+     *
+     * Per [RFC 7049: 2.2. Indefinite Lengths for Some Major Types](https://tools.ietf.org/html/rfc7049#section-2.2):
+     * > Four CBOR items (arrays, maps, byte strings, and text strings) can be encoded with an indefinite length
+     */
+    private fun isIndefinite(): Boolean {
+        val majorType = curByte and 0b111_00000
+        val value = curByte and 0b000_11111
+
+        return value == ADDITIONAL_INFORMATION_INDEFINITE_LENGTH &&
+            (majorType == HEADER_ARRAY || majorType == HEADER_MAP ||
+                majorType == HEADER_BYTE_STRING || majorType == HEADER_STRING)
+    }
+
+    /**
+     * Determines the length of the CBOR item represented by [curByte]; length has specific meaning based on the type:
+     *
+     * | Major type          | Length represents number of... |
+     * |---------------------|--------------------------------|
+     * | 0. unsigned integer | bytes                          |
+     * | 1. negative integer | bytes                          |
+     * | 2. byte string      | bytes                          |
+     * | 3. string           | bytes                          |
+     * | 4. array            | data items (values)            |
+     * | 5. map              | sub-items (keys + values)      |
+     * | 6. tag              | bytes                          |
+     */
+    private fun elementLength(): Int {
+        val majorType = curByte and 0b111_00000
+        val additionalInformation = curByte and 0b000_11111
+
+        return when (majorType) {
+            HEADER_BYTE_STRING, HEADER_STRING, HEADER_ARRAY -> readNumber().toInt()
+            HEADER_MAP -> readNumber().toInt() * 2
+            else -> when (additionalInformation) {
+                24 -> 1
+                25 -> 2
+                26 -> 4
+                27 -> 8
+                else -> 0
+            }
+        }
+    }
+
+    /**
+     * Indefinite-length byte sequences contain an unknown number of fixed-length byte sequences (chunks).
+     *
+     * @return [ByteArray] containing all of the concatenated bytes found in the buffer.
+     */
+    private fun readIndefiniteLengthBytes(): ByteArray {
+        val byteStrings = mutableListOf<ByteArray>()
+        do {
+            byteStrings.add(readBytes())
+            readByte()
+        } while (!isEnd())
+        return byteStrings.flatten()
+    }
+}
+
+private fun Iterable<ByteArray>.flatten(): ByteArray {
+    val output = ByteArray(sumOf { it.size })
+    var position = 0
+    for (chunk in this) {
+        chunk.copyInto(output, position)
+        position += chunk.size
+    }
+
+    return output
+}
+
+
+private class CborMapReader(cbor: Cbor, decoder: CborParser) : CborListReader(cbor, decoder) {
+    override fun skipBeginToken(objectTags: ULongArray?) =
+        setSize(parser.startMap(tags?.let { if (objectTags == null) it else ulongArrayOf(*it, *objectTags) }
+            ?: objectTags) * 2)
+}
+
+private open class CborListReader(cbor: Cbor, decoder: CborParser) : CborReader(cbor, decoder) {
+    private var ind = 0
+
+    override fun skipBeginToken(objectTags: ULongArray?) =
+        setSize(parser.startArray(tags?.let { if (objectTags == null) it else ulongArrayOf(*it, *objectTags) }
+            ?: objectTags))
+
+    override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+        return if (!finiteMode && parser.isEnd() || (finiteMode && ind >= size)) CompositeDecoder.DECODE_DONE else
+            ind++.also {
+                decodeByteArrayAsByteString = descriptor.isByteString(it)
+            }
+    }
+}
+
+
+private val normalizeBaseBits = SINGLE_PRECISION_NORMALIZE_BASE.toBits()
+
+
+/*
+ * For details about half-precision floating-point numbers see https://tools.ietf.org/html/rfc7049#appendix-D
+ */
+private fun floatFromHalfBits(bits: Short): Float {
+    val intBits = bits.toInt()
+
+    val negative = (intBits and 0x8000) != 0
+    val halfExp = intBits shr 10 and HALF_PRECISION_MAX_EXPONENT
+    val halfMant = intBits and HALF_PRECISION_MAX_MANTISSA
+
+    val exp: Int
+    val mant: Int
+
+    when (halfExp) {
+        HALF_PRECISION_MAX_EXPONENT -> {
+            // if exponent maximal - value is NaN or Infinity
+            exp = SINGLE_PRECISION_MAX_EXPONENT
+            mant = halfMant
+        }
+
+        0 -> {
+            if (halfMant == 0) {
+                // if exponent and mantissa are zero - value is zero
+                mant = 0
+                exp = 0
+            } else {
+                // if exponent is zero and mantissa non-zero - value denormalized. normalize it
+                var res = Float.fromBits(normalizeBaseBits + halfMant)
+                res -= SINGLE_PRECISION_NORMALIZE_BASE
+                return if (negative) -res else res
+            }
+        }
+
+        else -> {
+            // normalized value
+            exp = (halfExp + (SINGLE_PRECISION_EXPONENT_BIAS - HALF_PRECISION_EXPONENT_BIAS))
+            mant = halfMant
+        }
+    }
+
+    val res = Float.fromBits((exp shl 23) or (mant shl 13))
+    return if (negative) -res else res
+}
+
+
+@OptIn(ExperimentalSerializationApi::class)
+private fun SerialDescriptor.getElementNameForCborLabel(label: Long): String? {
+    return elementNames.firstOrNull { getCborLabel(getElementIndex(it)) == label }
+}
+
+
+@OptIn(ExperimentalSerializationApi::class)
+private fun SerialDescriptor.getElementIndexOrThrow(name: String): Int {
+    val index = getElementIndex(name)
+    if (index == CompositeDecoder.UNKNOWN_NAME)
+        throw SerializationException(
+            "$serialName does not contain element with name '$name." +
+                " You can enable 'CborBuilder.ignoreUnknownKeys' property to ignore unknown keys"
+        )
+    return index
+}
diff --git a/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/Encoder.kt b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/Encoder.kt
new file mode 100644
index 0000000..eb5fc55
--- /dev/null
+++ b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/Encoder.kt
@@ -0,0 +1,332 @@
+/*
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+@file:OptIn(ExperimentalSerializationApi::class, ExperimentalUnsignedTypes::class)
+
+package kotlinx.serialization.cbor.internal
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.cbor.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.modules.*
+import kotlin.experimental.*
+
+
+//value classes are only inlined on the JVM, so we use a typealias and extensions instead
+private typealias Stack = MutableList<CborWriter.Data>
+
+private fun Stack(initial: CborWriter.Data): Stack = mutableListOf(initial)
+private fun Stack.push(value: CborWriter.Data) = add(value)
+private fun Stack.pop() = removeLast()
+private fun Stack.peek() = last()
+
+// Writes class as map [fieldName, fieldValue]
+// Split implementation to optimize base case
+internal sealed class CborWriter(
+    override val cbor: Cbor,
+    protected val output: ByteArrayOutput,
+) : AbstractEncoder(), CborEncoder {
+    protected var isClass = false
+
+    protected var encodeByteArrayAsByteString = false
+
+    class Data(val bytes: ByteArrayOutput, var elementCount: Int)
+
+    protected abstract fun getDestination(): ByteArrayOutput
+
+    override val serializersModule: SerializersModule
+        get() = cbor.serializersModule
+
+
+    @OptIn(ExperimentalSerializationApi::class)
+    override fun <T> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) {
+
+        if ((encodeByteArrayAsByteString || cbor.configuration.alwaysUseByteString)
+            && serializer.descriptor == ByteArraySerializer().descriptor
+        ) {
+            getDestination().encodeByteString(value as ByteArray)
+        } else {
+            encodeByteArrayAsByteString = encodeByteArrayAsByteString || serializer.descriptor.isInlineByteString()
+            super<AbstractEncoder>.encodeSerializableValue(serializer, value)
+        }
+    }
+
+    override fun shouldEncodeElementDefault(descriptor: SerialDescriptor, index: Int): Boolean =
+        cbor.configuration.encodeDefaults
+
+    protected abstract fun incrementChildren()
+
+    override fun encodeString(value: String) {
+        getDestination().encodeString(value)
+    }
+
+
+    override fun encodeFloat(value: Float) {
+        getDestination().encodeFloat(value)
+    }
+
+
+    override fun encodeDouble(value: Double) {
+        getDestination().encodeDouble(value)
+    }
+
+
+    override fun encodeChar(value: Char) {
+        getDestination().encodeNumber(value.code.toLong())
+    }
+
+
+    override fun encodeByte(value: Byte) {
+        getDestination().encodeNumber(value.toLong())
+    }
+
+
+    override fun encodeShort(value: Short) {
+        getDestination().encodeNumber(value.toLong())
+    }
+
+    override fun encodeInt(value: Int) {
+        getDestination().encodeNumber(value.toLong())
+    }
+
+
+    override fun encodeLong(value: Long) {
+        getDestination().encodeNumber(value)
+    }
+
+
+    override fun encodeBoolean(value: Boolean) {
+        getDestination().encodeBoolean(value)
+    }
+
+
+    override fun encodeNull() {
+        if (isClass) getDestination().encodeEmptyMap()
+        else getDestination().encodeNull()
+    }
+
+    @OptIn(ExperimentalSerializationApi::class) // KT-46731
+    override fun encodeEnum(
+        enumDescriptor: SerialDescriptor,
+        index: Int
+    ) {
+        getDestination().encodeString(enumDescriptor.getElementName(index))
+    }
+
+    override fun encodeElement(descriptor: SerialDescriptor, index: Int): Boolean {
+        val destination = getDestination()
+        isClass = descriptor.getElementDescriptor(index).kind == StructureKind.CLASS
+        encodeByteArrayAsByteString = descriptor.isByteString(index)
+
+        val name = descriptor.getElementName(index)
+
+
+        if (!descriptor.hasArrayTag()) {
+            if (cbor.configuration.encodeKeyTags) descriptor.getKeyTags(index)?.forEach { destination.encodeTag(it) }
+
+            if ((descriptor.kind !is StructureKind.LIST) && (descriptor.kind !is StructureKind.MAP) && (descriptor.kind !is PolymorphicKind)) {
+                //indices are put into the name field. we don't want to write those, as it would result in double writes
+                val cborLabel = descriptor.getCborLabel(index)
+                if (cbor.configuration.preferCborLabelsOverNames && cborLabel != null) {
+                    destination.encodeNumber(cborLabel)
+                } else {
+                    destination.encodeString(name)
+                }
+            }
+        }
+
+        if (cbor.configuration.encodeValueTags) {
+            descriptor.getValueTags(index)?.forEach { destination.encodeTag(it) }
+        }
+        incrementChildren() // needed for definite len encoding, NOOP for indefinite length encoding
+        return true
+    }
+}
+
+
+// optimized indefinite length encoder
+internal class IndefiniteLengthCborWriter(cbor: Cbor, output: ByteArrayOutput) : CborWriter(
+    cbor, output
+) {
+
+    override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder {
+        if (cbor.configuration.encodeObjectTags) descriptor.getObjectTags()?.forEach {
+            output.encodeTag(it)
+        }
+        if (descriptor.hasArrayTag()) {
+            output.startArray()
+        } else {
+            when (descriptor.kind) {
+                StructureKind.LIST, is PolymorphicKind -> output.startArray()
+                is StructureKind.MAP -> output.startMap()
+                else -> output.startMap()
+            }
+        }
+        return this
+    }
+
+    override fun endStructure(descriptor: SerialDescriptor) {
+        output.end()
+    }
+
+    override fun getDestination(): ByteArrayOutput = output
+
+
+    override fun incrementChildren() {/*NOOP*/
+    }
+
+}
+
+//optimized definite length encoder
+internal class DefiniteLengthCborWriter(cbor: Cbor, output: ByteArrayOutput) : CborWriter(cbor, output) {
+
+    private val structureStack = Stack(Data(output, -1))
+    override fun getDestination(): ByteArrayOutput =
+        structureStack.peek().bytes
+
+
+    override fun incrementChildren() {
+        structureStack.peek().elementCount++
+    }
+
+    override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder {
+        val current = Data(ByteArrayOutput(), 0)
+        structureStack.push(current)
+        return this
+    }
+
+    override fun endStructure(descriptor: SerialDescriptor) {
+        val completedCurrent = structureStack.pop()
+
+        val accumulator = getDestination()
+
+        val numChildren = completedCurrent.elementCount
+
+        if (cbor.configuration.encodeObjectTags) descriptor.getObjectTags()?.forEach {
+            accumulator.encodeTag(it)
+        }
+
+        if (descriptor.hasArrayTag()) {
+            accumulator.startArray(numChildren.toULong())
+        } else {
+            when (descriptor.kind) {
+                StructureKind.LIST, is PolymorphicKind -> accumulator.startArray(numChildren.toULong())
+                is StructureKind.MAP -> accumulator.startMap((numChildren / 2).toULong())
+                else -> accumulator.startMap((numChildren).toULong())
+            }
+        }
+        accumulator.copyFrom(completedCurrent.bytes)
+    }
+}
+
+
+private fun ByteArrayOutput.startArray() = write(BEGIN_ARRAY)
+
+private fun ByteArrayOutput.startArray(size: ULong) {
+    composePositiveInline(size, HEADER_ARRAY)
+}
+
+private fun ByteArrayOutput.startMap() = write(BEGIN_MAP)
+
+private fun ByteArrayOutput.startMap(size: ULong) {
+    composePositiveInline(size, HEADER_MAP)
+}
+
+private fun ByteArrayOutput.encodeTag(tag: ULong) {
+    composePositiveInline(tag, HEADER_TAG)
+}
+
+internal fun ByteArrayOutput.end() = write(BREAK)
+
+internal fun ByteArrayOutput.encodeNull() = write(NULL)
+
+internal fun ByteArrayOutput.encodeEmptyMap() = write(EMPTY_MAP)
+
+internal fun ByteArrayOutput.writeByte(byteValue: Int) = write(byteValue)
+
+internal fun ByteArrayOutput.encodeBoolean(value: Boolean) = write(if (value) TRUE else FALSE)
+
+internal fun ByteArrayOutput.encodeNumber(value: Long) = write(composeNumber(value))
+
+internal fun ByteArrayOutput.encodeByteString(data: ByteArray) {
+    this.encodeByteArray(data, HEADER_BYTE_STRING)
+}
+
+internal fun ByteArrayOutput.encodeString(value: String) {
+    this.encodeByteArray(value.encodeToByteArray(), HEADER_STRING)
+}
+
+internal fun ByteArrayOutput.encodeByteArray(data: ByteArray, type: Int) {
+    composePositiveInline(data.size.toULong(), type)
+    write(data)
+}
+
+internal fun ByteArrayOutput.encodeFloat(value: Float) {
+    write(NEXT_FLOAT)
+    val bits = value.toRawBits()
+    for (i in 0..3) {
+        write((bits shr (24 - 8 * i)) and 0xFF)
+    }
+}
+
+internal fun ByteArrayOutput.encodeDouble(value: Double) {
+    write(NEXT_DOUBLE)
+    val bits = value.toRawBits()
+    for (i in 0..7) {
+        write(((bits shr (56 - 8 * i)) and 0xFF).toInt())
+    }
+}
+
+//don't know why, but if the negative branch is also optimized and everything operates directly on the ByteArrayOutput it gets slower
+private fun composeNumber(value: Long): ByteArray =
+    if (value >= 0) composePositive(value.toULong()) else composeNegative(value)
+
+private fun ByteArrayOutput.composePositiveInline(value: ULong, mod: Int) = when (value) {
+    in 0u..23u -> writeByte(value.toInt() or mod)
+    in 24u..UByte.MAX_VALUE.toUInt() -> {
+        writeByte(24 or mod)
+        writeByte(value.toInt())
+    }
+
+    in (UByte.MAX_VALUE.toUInt() + 1u)..UShort.MAX_VALUE.toUInt() -> encodeToInline(value, 2, 25 or mod)
+    in (UShort.MAX_VALUE.toUInt() + 1u)..UInt.MAX_VALUE -> encodeToInline(value, 4, 26 or mod)
+    else -> encodeToInline(value, 8, 27 or mod)
+}
+
+
+private fun composePositive(value: ULong): ByteArray = when (value) {
+    in 0u..23u -> byteArrayOf(value.toByte())
+    in 24u..UByte.MAX_VALUE.toUInt() -> byteArrayOf(24, value.toByte())
+    in (UByte.MAX_VALUE.toUInt() + 1u)..UShort.MAX_VALUE.toUInt() -> encodeToByteArray(value, 2, 25)
+    in (UShort.MAX_VALUE.toUInt() + 1u)..UInt.MAX_VALUE -> encodeToByteArray(value, 4, 26)
+    else -> encodeToByteArray(value, 8, 27)
+}
+
+
+private fun ByteArrayOutput.encodeToInline(value: ULong, bytes: Int, tag: Int) {
+    val limit = bytes * 8 - 8
+    writeByte(tag)
+    for (i in 0 until bytes) {
+        writeByte(((value shr (limit - 8 * i)) and 0xFFu).toInt())
+    }
+}
+
+private fun encodeToByteArray(value: ULong, bytes: Int, tag: Byte): ByteArray {
+    val result = ByteArray(bytes + 1)
+    val limit = bytes * 8 - 8
+    result[0] = tag
+    for (i in 0 until bytes) {
+        result[i + 1] = ((value shr (limit - 8 * i)) and 0xFFu).toByte()
+    }
+    return result
+}
+
+private fun composeNegative(value: Long): ByteArray {
+    val aVal = if (value == Long.MIN_VALUE) Long.MAX_VALUE else -1 - value
+    val data = composePositive(aVal.toULong())
+    data[0] = data[0] or HEADER_NEGATIVE
+    return data
+}
+
diff --git a/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/Encoding.kt b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/Encoding.kt
index b77a18c..63b8f0a 100644
--- a/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/Encoding.kt
+++ b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/Encoding.kt
@@ -1,691 +1,80 @@
 /*
  * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
  */
-@file:OptIn(ExperimentalSerializationApi::class)
+@file:OptIn(ExperimentalSerializationApi::class, ExperimentalUnsignedTypes::class)
 
 package kotlinx.serialization.cbor.internal
 
 import kotlinx.serialization.*
-import kotlinx.serialization.builtins.*
 import kotlinx.serialization.cbor.*
 import kotlinx.serialization.descriptors.*
-import kotlinx.serialization.encoding.*
-import kotlinx.serialization.modules.*
-import kotlin.experimental.*
 
-private const val FALSE = 0xf4
-private const val TRUE = 0xf5
-private const val NULL = 0xf6
+internal const val FALSE = 0xf4
+internal const val TRUE = 0xf5
+internal const val NULL = 0xf6
+internal const val EMPTY_MAP = 0xa0
 
-private const val NEXT_HALF = 0xf9
-private const val NEXT_FLOAT = 0xfa
-private const val NEXT_DOUBLE = 0xfb
+internal const val NEXT_HALF = 0xf9
+internal const val NEXT_FLOAT = 0xfa
+internal const val NEXT_DOUBLE = 0xfb
 
-private const val BEGIN_ARRAY = 0x9f
-private const val BEGIN_MAP = 0xbf
-private const val BREAK = 0xff
+internal const val BEGIN_ARRAY = 0x9f
+internal const val BEGIN_MAP = 0xbf
+internal const val BREAK = 0xff
 
-private const val ADDITIONAL_INFORMATION_INDEFINITE_LENGTH = 0x1f
+internal const val ADDITIONAL_INFORMATION_INDEFINITE_LENGTH = 0x1f
 
-private const val HEADER_BYTE_STRING: Byte = 0b010_00000
-private const val HEADER_STRING: Byte = 0b011_00000
-private const val HEADER_NEGATIVE: Byte = 0b001_00000
-private const val HEADER_ARRAY: Int = 0b100_00000
-private const val HEADER_MAP: Int = 0b101_00000
-private const val HEADER_TAG: Int = 0b110_00000
+internal const val HEADER_BYTE_STRING: Int = 0b010_00000
+internal const val HEADER_STRING: Int = 0b011_00000
+internal const val HEADER_NEGATIVE: Byte = 0b001_00000
+internal const val HEADER_ARRAY: Int = 0b100_00000
+internal const val HEADER_MAP: Int = 0b101_00000
+internal const val HEADER_TAG: Int = 0b110_00000
 
 /** Value to represent an indefinite length CBOR item within a "length stack". */
-private const val LENGTH_STACK_INDEFINITE = -1
+internal const val LENGTH_STACK_INDEFINITE = -1
 
-private const val HALF_PRECISION_EXPONENT_BIAS = 15
-private const val HALF_PRECISION_MAX_EXPONENT = 0x1f
-private const val HALF_PRECISION_MAX_MANTISSA = 0x3ff
+internal const val HALF_PRECISION_EXPONENT_BIAS = 15
+internal const val HALF_PRECISION_MAX_EXPONENT = 0x1f
+internal const val HALF_PRECISION_MAX_MANTISSA = 0x3ff
 
-private const val SINGLE_PRECISION_EXPONENT_BIAS = 127
-private const val SINGLE_PRECISION_MAX_EXPONENT = 0xFF
+internal const val SINGLE_PRECISION_EXPONENT_BIAS = 127
+internal const val SINGLE_PRECISION_MAX_EXPONENT = 0xFF
 
-private const val SINGLE_PRECISION_NORMALIZE_BASE = 0.5f
-
-// Differs from List only in start byte
-private class CborMapWriter(cbor: Cbor, encoder: CborEncoder) : CborListWriter(cbor, encoder) {
-    override fun writeBeginToken() = encoder.startMap()
-}
-
-// Writes all elements consequently, except size - CBOR supports maps and arrays of indefinite length
-private open class CborListWriter(cbor: Cbor, encoder: CborEncoder) : CborWriter(cbor, encoder) {
-    override fun writeBeginToken() = encoder.startArray()
-
-    override fun encodeElement(descriptor: SerialDescriptor, index: Int): Boolean = true
-}
-
-// Writes class as map [fieldName, fieldValue]
-internal open class CborWriter(private val cbor: Cbor, protected val encoder: CborEncoder) : AbstractEncoder() {
-    override val serializersModule: SerializersModule
-        get() = cbor.serializersModule
-
-    private var encodeByteArrayAsByteString = false
-
-    @OptIn(ExperimentalSerializationApi::class)
-    override fun <T> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) {
-        if (encodeByteArrayAsByteString && serializer.descriptor == ByteArraySerializer().descriptor) {
-            encoder.encodeByteString(value as ByteArray)
-        } else {
-            encodeByteArrayAsByteString = encodeByteArrayAsByteString || serializer.descriptor.isInlineByteString()
-
-            super.encodeSerializableValue(serializer, value)
-        }
-    }
-
-    override fun shouldEncodeElementDefault(descriptor: SerialDescriptor, index: Int): Boolean = cbor.encodeDefaults
-
-    protected open fun writeBeginToken() = encoder.startMap()
-
-    //todo: Write size of map or array if known
-    @OptIn(ExperimentalSerializationApi::class)
-    override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder {
-        val writer = when (descriptor.kind) {
-            StructureKind.LIST, is PolymorphicKind -> CborListWriter(cbor, encoder)
-            StructureKind.MAP -> CborMapWriter(cbor, encoder)
-            else -> CborWriter(cbor, encoder)
-        }
-        writer.writeBeginToken()
-        return writer
-    }
-
-    override fun endStructure(descriptor: SerialDescriptor) = encoder.end()
-
-    override fun encodeElement(descriptor: SerialDescriptor, index: Int): Boolean {
-        encodeByteArrayAsByteString = descriptor.isByteString(index)
-        val name = descriptor.getElementName(index)
-        encoder.encodeString(name)
-        return true
-    }
-
-    override fun encodeString(value: String) = encoder.encodeString(value)
-
-    override fun encodeFloat(value: Float) = encoder.encodeFloat(value)
-    override fun encodeDouble(value: Double) = encoder.encodeDouble(value)
-
-    override fun encodeChar(value: Char) = encoder.encodeNumber(value.code.toLong())
-    override fun encodeByte(value: Byte) = encoder.encodeNumber(value.toLong())
-    override fun encodeShort(value: Short) = encoder.encodeNumber(value.toLong())
-    override fun encodeInt(value: Int) = encoder.encodeNumber(value.toLong())
-    override fun encodeLong(value: Long) = encoder.encodeNumber(value)
-
-    override fun encodeBoolean(value: Boolean) = encoder.encodeBoolean(value)
-
-    override fun encodeNull() = encoder.encodeNull()
-
-    @OptIn(ExperimentalSerializationApi::class) // KT-46731
-    override fun encodeEnum(
-        enumDescriptor: SerialDescriptor,
-        index: Int
-    ) =
-        encoder.encodeString(enumDescriptor.getElementName(index))
-}
-
-// For details of representation, see https://tools.ietf.org/html/rfc7049#section-2.1
-internal class CborEncoder(private val output: ByteArrayOutput) {
-
-    fun startArray() = output.write(BEGIN_ARRAY)
-    fun startMap() = output.write(BEGIN_MAP)
-    fun end() = output.write(BREAK)
-
-    fun encodeNull() = output.write(NULL)
-
-    fun encodeBoolean(value: Boolean) = output.write(if (value) TRUE else FALSE)
-
-    fun encodeNumber(value: Long) = output.write(composeNumber(value))
-
-    fun encodeByteString(data: ByteArray) {
-        encodeByteArray(data, HEADER_BYTE_STRING)
-    }
-
-    fun encodeString(value: String) {
-        encodeByteArray(value.encodeToByteArray(), HEADER_STRING)
-    }
-
-    private fun encodeByteArray(data: ByteArray, type: Byte) {
-        val header = composeNumber(data.size.toLong())
-        header[0] = header[0] or type
-        output.write(header)
-        output.write(data)
-    }
-
-    fun encodeFloat(value: Float) {
-        output.write(NEXT_FLOAT)
-        val bits = value.toRawBits()
-        for (i in 0..3) {
-            output.write((bits shr (24 - 8 * i)) and 0xFF)
-        }
-    }
-
-    fun encodeDouble(value: Double) {
-        output.write(NEXT_DOUBLE)
-        val bits = value.toRawBits()
-        for (i in 0..7) {
-            output.write(((bits shr (56 - 8 * i)) and 0xFF).toInt())
-        }
-    }
-
-    private fun composeNumber(value: Long): ByteArray =
-        if (value >= 0) composePositive(value.toULong()) else composeNegative(value)
-
-    private fun composePositive(value: ULong): ByteArray = when (value) {
-        in 0u..23u -> byteArrayOf(value.toByte())
-        in 24u..UByte.MAX_VALUE.toUInt() -> byteArrayOf(24, value.toByte())
-        in (UByte.MAX_VALUE.toUInt() + 1u)..UShort.MAX_VALUE.toUInt() -> encodeToByteArray(value, 2, 25)
-        in (UShort.MAX_VALUE.toUInt() + 1u)..UInt.MAX_VALUE -> encodeToByteArray(value, 4, 26)
-        else -> encodeToByteArray(value, 8, 27)
-    }
-
-    private fun encodeToByteArray(value: ULong, bytes: Int, tag: Byte): ByteArray {
-        val result = ByteArray(bytes + 1)
-        val limit = bytes * 8 - 8
-        result[0] = tag
-        for (i in 0 until bytes) {
-            result[i + 1] = ((value shr (limit - 8 * i)) and 0xFFu).toByte()
-        }
-        return result
-    }
-
-    private fun composeNegative(value: Long): ByteArray {
-        val aVal = if (value == Long.MIN_VALUE) Long.MAX_VALUE else -1 - value
-        val data = composePositive(aVal.toULong())
-        data[0] = data[0] or HEADER_NEGATIVE
-        return data
-    }
-}
-
-private class CborMapReader(cbor: Cbor, decoder: CborDecoder) : CborListReader(cbor, decoder) {
-    override fun skipBeginToken() = setSize(decoder.startMap() * 2)
-}
-
-private open class CborListReader(cbor: Cbor, decoder: CborDecoder) : CborReader(cbor, decoder) {
-    private var ind = 0
-
-    override fun skipBeginToken() = setSize(decoder.startArray())
-
-    override fun decodeElementIndex(descriptor: SerialDescriptor) = if (!finiteMode && decoder.isEnd() || (finiteMode && ind >= size)) CompositeDecoder.DECODE_DONE else ind++
-}
-
-internal open class CborReader(private val cbor: Cbor, protected val decoder: CborDecoder) : AbstractDecoder() {
-
-    protected var size = -1
-        private set
-    protected var finiteMode = false
-        private set
-    private var readProperties: Int = 0
-
-    private var decodeByteArrayAsByteString = false
-
-    protected fun setSize(size: Int) {
-        if (size >= 0) {
-            finiteMode = true
-            this.size = size
-        }
-    }
-
-    override val serializersModule: SerializersModule
-        get() = cbor.serializersModule
-
-    protected open fun skipBeginToken() = setSize(decoder.startMap())
-
-    @OptIn(ExperimentalSerializationApi::class)
-    override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
-        val re = when (descriptor.kind) {
-            StructureKind.LIST, is PolymorphicKind -> CborListReader(cbor, decoder)
-            StructureKind.MAP -> CborMapReader(cbor, decoder)
-            else -> CborReader(cbor, decoder)
-        }
-        re.skipBeginToken()
-        return re
-    }
-
-    override fun endStructure(descriptor: SerialDescriptor) {
-        if (!finiteMode) decoder.end()
-    }
-
-    override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
-        val index = if (cbor.ignoreUnknownKeys) {
-            val knownIndex: Int
-            while (true) {
-                if (isDone()) return CompositeDecoder.DECODE_DONE
-                val elemName = decoder.nextString()
-                readProperties++
-
-                val index = descriptor.getElementIndex(elemName)
-                if (index == CompositeDecoder.UNKNOWN_NAME) {
-                    decoder.skipElement()
-                } else {
-                    knownIndex = index
-                    break
-                }
-            }
-            knownIndex
-        } else {
-            if (isDone()) return CompositeDecoder.DECODE_DONE
-            val elemName = decoder.nextString()
-            readProperties++
-            descriptor.getElementIndexOrThrow(elemName)
-        }
-
-        decodeByteArrayAsByteString = descriptor.isByteString(index)
-        return index
-    }
-
-    @OptIn(ExperimentalSerializationApi::class)
-    override fun <T> decodeSerializableValue(deserializer: DeserializationStrategy<T>): T {
-        return if (decodeByteArrayAsByteString && deserializer.descriptor == ByteArraySerializer().descriptor) {
-            @Suppress("UNCHECKED_CAST")
-            decoder.nextByteString() as T
-        } else {
-            decodeByteArrayAsByteString = decodeByteArrayAsByteString || deserializer.descriptor.isInlineByteString()
-            super.decodeSerializableValue(deserializer)
-        }
-    }
-
-    override fun decodeString() = decoder.nextString()
-
-    override fun decodeNotNullMark(): Boolean = !decoder.isNull()
-
-    override fun decodeDouble() = decoder.nextDouble()
-    override fun decodeFloat() = decoder.nextFloat()
-
-    override fun decodeBoolean() = decoder.nextBoolean()
-
-    override fun decodeByte() = decoder.nextNumber().toByte()
-    override fun decodeShort() = decoder.nextNumber().toShort()
-    override fun decodeChar() = decoder.nextNumber().toInt().toChar()
-    override fun decodeInt() = decoder.nextNumber().toInt()
-    override fun decodeLong() = decoder.nextNumber()
-
-    override fun decodeNull() = decoder.nextNull()
-
-    override fun decodeEnum(enumDescriptor: SerialDescriptor): Int =
-        enumDescriptor.getElementIndexOrThrow(decoder.nextString())
-
-    private fun isDone(): Boolean = !finiteMode && decoder.isEnd() || (finiteMode && readProperties >= size)
-}
-
-internal class CborDecoder(private val input: ByteArrayInput) {
-    private var curByte: Int = -1
-
-    init {
-        readByte()
-    }
-
-    private fun readByte(): Int {
-        curByte = input.read()
-        return curByte
-    }
-
-    fun isEof() = curByte == -1
-
-    private fun skipByte(expected: Int) {
-        if (curByte != expected) throw CborDecodingException("byte ${printByte(expected)}", curByte)
-        readByte()
-    }
-
-    fun isNull() = curByte == NULL
-
-    fun nextNull(): Nothing? {
-        skipOverTags()
-        skipByte(NULL)
-        return null
-    }
-
-    fun nextBoolean(): Boolean {
-        skipOverTags()
-        val ans = when (curByte) {
-            TRUE -> true
-            FALSE -> false
-            else -> throw CborDecodingException("boolean value", curByte)
-        }
-        readByte()
-        return ans
-    }
-
-    fun startArray() = startSized(BEGIN_ARRAY, HEADER_ARRAY, "array")
-
-    fun startMap() = startSized(BEGIN_MAP, HEADER_MAP, "map")
-
-    private fun startSized(unboundedHeader: Int, boundedHeaderMask: Int, collectionType: String): Int {
-        skipOverTags()
-        if (curByte == unboundedHeader) {
-            skipByte(unboundedHeader)
-            return -1
-        }
-        if ((curByte and 0b111_00000) != boundedHeaderMask)
-            throw CborDecodingException("start of $collectionType", curByte)
-        val size = readNumber().toInt()
-        readByte()
-        return size
-    }
-
-    fun isEnd() = curByte == BREAK
-
-    fun end() = skipByte(BREAK)
-
-    fun nextByteString(): ByteArray {
-        skipOverTags()
-        if ((curByte and 0b111_00000) != HEADER_BYTE_STRING.toInt())
-            throw CborDecodingException("start of byte string", curByte)
-        val arr = readBytes()
-        readByte()
-        return arr
-    }
-
-    fun nextString(): String {
-        skipOverTags()
-        if ((curByte and 0b111_00000) != HEADER_STRING.toInt())
-            throw CborDecodingException("start of string", curByte)
-        val arr = readBytes()
-        val ans = arr.decodeToString()
-        readByte()
-        return ans
-    }
-
-    private fun readBytes(): ByteArray =
-        if (curByte and 0b000_11111 == ADDITIONAL_INFORMATION_INDEFINITE_LENGTH) {
-            readByte()
-            readIndefiniteLengthBytes()
-        } else {
-            val strLen = readNumber().toInt()
-            input.readExactNBytes(strLen)
-        }
-
-    private fun skipOverTags() {
-        while ((curByte and 0b111_00000) == HEADER_TAG) {
-            readNumber() // This is the tag number
-            readByte()
-        }
-    }
-
-    fun nextNumber(): Long {
-        skipOverTags()
-        val res = readNumber()
-        readByte()
-        return res
-    }
-
-    private fun readNumber(): Long {
-        val value = curByte and 0b000_11111
-        val negative = (curByte and 0b111_00000) == HEADER_NEGATIVE.toInt()
-        val bytesToRead = when (value) {
-            24 -> 1
-            25 -> 2
-            26 -> 4
-            27 -> 8
-            else -> 0
-        }
-        if (bytesToRead == 0) {
-            return if (negative) -(value + 1).toLong()
-            else value.toLong()
-        }
-        val res = input.readExact(bytesToRead)
-        return if (negative) -(res + 1)
-        else res
-    }
-
-    private fun ByteArrayInput.readExact(bytes: Int): Long {
-        val arr = readExactNBytes(bytes)
-        var result = 0L
-        for (i in 0 until bytes) {
-            result = (result shl 8) or (arr[i].toInt() and 0xFF).toLong()
-        }
-        return result
-    }
-
-    private fun ByteArrayInput.readExactNBytes(bytesCount: Int): ByteArray {
-        if (bytesCount > availableBytes) {
-            error("Unexpected EOF, available $availableBytes bytes, requested: $bytesCount")
-        }
-        val array = ByteArray(bytesCount)
-        read(array, 0, bytesCount)
-        return array
-    }
-
-    fun nextFloat(): Float {
-        skipOverTags()
-        val res = when (curByte) {
-            NEXT_FLOAT -> Float.fromBits(readInt())
-            NEXT_HALF -> floatFromHalfBits(readShort())
-            else -> throw CborDecodingException("float header", curByte)
-        }
-        readByte()
-        return res
-    }
-
-    fun nextDouble(): Double {
-        skipOverTags()
-        val res = when (curByte) {
-            NEXT_DOUBLE -> Double.fromBits(readLong())
-            NEXT_FLOAT -> Float.fromBits(readInt()).toDouble()
-            NEXT_HALF -> floatFromHalfBits(readShort()).toDouble()
-            else -> throw CborDecodingException("double header", curByte)
-        }
-        readByte()
-        return res
-    }
-
-    private fun readLong(): Long {
-        var result = 0L
-        for (i in 0..7) {
-            val byte = input.read()
-            result = (result shl 8) or byte.toLong()
-        }
-        return result
-    }
-
-    private fun readShort(): Short {
-        val highByte = input.read()
-        val lowByte = input.read()
-        return (highByte shl 8 or lowByte).toShort()
-    }
-
-    private fun readInt(): Int {
-        var result = 0
-        for (i in 0..3) {
-            val byte = input.read()
-            result = (result shl 8) or byte
-        }
-        return result
-    }
-
-    /**
-     * Skips the current value element. Bytes are processed to determine the element type (and corresponding length), to
-     * determine how many bytes to skip.
-     *
-     * For primitive (finite length) elements (e.g. unsigned integer, text string), their length is read and
-     * corresponding number of bytes are skipped.
-     *
-     * For elements that contain children (e.g. array, map), the child count is read and added to a "length stack"
-     * (which represents the "number of elements" at each depth of the CBOR data structure). When a child element has
-     * been skipped, the "length stack" is [pruned][prune]. For indefinite length elements, a special marker is added to
-     * the "length stack" which is only popped from the "length stack" when a CBOR [break][isEnd] is encountered.
-     */
-    fun skipElement() {
-        val lengthStack = mutableListOf<Int>()
-
-        skipOverTags()
-
-        do {
-            if (isEof()) throw CborDecodingException("Unexpected EOF while skipping element")
-
-            if (isIndefinite()) {
-                lengthStack.add(LENGTH_STACK_INDEFINITE)
-            } else if (isEnd()) {
-                if (lengthStack.removeLastOrNull() != LENGTH_STACK_INDEFINITE)
-                    throw CborDecodingException("next data item", curByte)
-                prune(lengthStack)
-            } else {
-                val header = curByte and 0b111_00000
-                val length = elementLength()
-                if (header == HEADER_ARRAY || header == HEADER_MAP) {
-                    if (length > 0) lengthStack.add(length)
-                    skipOverTags()
-                } else {
-                    input.skip(length)
-                    prune(lengthStack)
-                }
-            }
-
-            readByte()
-        } while (lengthStack.isNotEmpty())
-    }
-
-    /**
-     * Removes an item from the top of the [lengthStack], cascading the removal if the item represents the last item
-     * (i.e. a length value of `1`) at its stack depth.
-     *
-     * For example, pruning a [lengthStack] of `[3, 2, 1, 1]` would result in `[3, 1]`.
-     */
-    private fun prune(lengthStack: MutableList<Int>) {
-        for (i in lengthStack.lastIndex downTo 0) {
-            when (lengthStack[i]) {
-                LENGTH_STACK_INDEFINITE -> break
-                1 -> lengthStack.removeAt(i)
-                else -> {
-                    lengthStack[i] = lengthStack[i] - 1
-                    break
-                }
-            }
-        }
-    }
-
-    /**
-     * Determines if [curByte] represents an indefinite length CBOR item.
-     *
-     * Per [RFC 7049: 2.2. Indefinite Lengths for Some Major Types](https://tools.ietf.org/html/rfc7049#section-2.2):
-     * > Four CBOR items (arrays, maps, byte strings, and text strings) can be encoded with an indefinite length
-     */
-    private fun isIndefinite(): Boolean {
-        val majorType = curByte and 0b111_00000
-        val value = curByte and 0b000_11111
-
-        return value == ADDITIONAL_INFORMATION_INDEFINITE_LENGTH &&
-            (majorType == HEADER_ARRAY || majorType == HEADER_MAP ||
-                majorType == HEADER_BYTE_STRING.toInt() || majorType == HEADER_STRING.toInt())
-    }
-
-    /**
-     * Determines the length of the CBOR item represented by [curByte]; length has specific meaning based on the type:
-     *
-     * | Major type          | Length represents number of... |
-     * |---------------------|--------------------------------|
-     * | 0. unsigned integer | bytes                          |
-     * | 1. negative integer | bytes                          |
-     * | 2. byte string      | bytes                          |
-     * | 3. string           | bytes                          |
-     * | 4. array            | data items (values)            |
-     * | 5. map              | sub-items (keys + values)      |
-     * | 6. tag              | bytes                          |
-     */
-    private fun elementLength(): Int {
-        val majorType = curByte and 0b111_00000
-        val additionalInformation = curByte and 0b000_11111
-
-        return when (majorType) {
-            HEADER_BYTE_STRING.toInt(), HEADER_STRING.toInt(), HEADER_ARRAY -> readNumber().toInt()
-            HEADER_MAP -> readNumber().toInt() * 2
-            else -> when (additionalInformation) {
-                24 -> 1
-                25 -> 2
-                26 -> 4
-                27 -> 8
-                else -> 0
-            }
-        }
-    }
+internal const val SINGLE_PRECISION_NORMALIZE_BASE = 0.5f
 
-    /**
-     * Indefinite-length byte sequences contain an unknown number of fixed-length byte sequences (chunks).
-     *
-     * @return [ByteArray] containing all of the concatenated bytes found in the buffer.
-     */
-    private fun readIndefiniteLengthBytes(): ByteArray {
-        val byteStrings = mutableListOf<ByteArray>()
-        do {
-            byteStrings.add(readBytes())
-            readByte()
-        } while (!isEnd())
-        return byteStrings.flatten()
-    }
-}
 
 @OptIn(ExperimentalSerializationApi::class)
-private fun SerialDescriptor.getElementIndexOrThrow(name: String): Int {
-    val index = getElementIndex(name)
-    if (index == CompositeDecoder.UNKNOWN_NAME)
-        throw SerializationException("$serialName does not contain element with name '$name." +
-            " You can enable 'CborBuilder.ignoreUnknownKeys' property to ignore unknown keys")
-    return index
-}
-
-private fun Iterable<ByteArray>.flatten(): ByteArray {
-    val output = ByteArray(sumOf { it.size })
-    var position = 0
-    for (chunk in this) {
-        chunk.copyInto(output, position)
-        position += chunk.size
-    }
-
-    return output
-}
-
-@OptIn(ExperimentalSerializationApi::class)
-private fun SerialDescriptor.isByteString(index: Int): Boolean {
+internal fun SerialDescriptor.isByteString(index: Int): Boolean {
     return getElementAnnotations(index).find { it is ByteString } != null
 }
 
-private fun SerialDescriptor.isInlineByteString(): Boolean {
+
+internal fun SerialDescriptor.isInlineByteString(): Boolean {
     // inline item classes should only have 1 item
     return isInline && isByteString(0)
 }
 
+@OptIn(ExperimentalSerializationApi::class)
+internal fun SerialDescriptor.getValueTags(index: Int): ULongArray? = findAnnotation<ValueTags>(index)?.tags
 
-private val normalizeBaseBits = SINGLE_PRECISION_NORMALIZE_BASE.toBits()
+@OptIn(ExperimentalSerializationApi::class)
+internal fun SerialDescriptor.getKeyTags(index: Int): ULongArray? = findAnnotation<KeyTags>(index)?.tags
 
+@OptIn(ExperimentalSerializationApi::class)
+internal fun SerialDescriptor.getCborLabel(index: Int): Long? = findAnnotation<CborLabel>(index)?.label
 
-/*
- * For details about half-precision floating-point numbers see https://tools.ietf.org/html/rfc7049#appendix-D
- */
-private fun floatFromHalfBits(bits: Short): Float {
-    val intBits = bits.toInt()
-
-    val negative = (intBits and 0x8000) != 0
-    val halfExp = intBits shr 10 and HALF_PRECISION_MAX_EXPONENT
-    val halfMant = intBits and HALF_PRECISION_MAX_MANTISSA
-
-    val exp: Int
-    val mant: Int
-
-    when (halfExp) {
-        HALF_PRECISION_MAX_EXPONENT -> {
-            // if exponent maximal - value is NaN or Infinity
-            exp = SINGLE_PRECISION_MAX_EXPONENT
-            mant = halfMant
-        }
-        0 -> {
-            if (halfMant == 0) {
-                // if exponent and mantissa are zero - value is zero
-                mant = 0
-                exp = 0
-            } else {
-                // if exponent is zero and mantissa non-zero - value denormalized. normalize it
-                var res = Float.fromBits(normalizeBaseBits + halfMant)
-                res -= SINGLE_PRECISION_NORMALIZE_BASE
-                return if (negative) -res else res
-            }
-        }
-        else -> {
-            // normalized value
-            exp = (halfExp + (SINGLE_PRECISION_EXPONENT_BIAS - HALF_PRECISION_EXPONENT_BIAS))
-            mant = halfMant
-        }
-    }
-
-    val res = Float.fromBits((exp shl 23) or (mant shl 13))
-    return if (negative) -res else res
+@OptIn(ExperimentalSerializationApi::class)
+internal fun SerialDescriptor.hasArrayTag(): Boolean {
+    return annotations.any { it is CborArray }
 }
+
+@OptIn(ExperimentalSerializationApi::class)
+internal inline fun <reified A : Annotation> SerialDescriptor.findAnnotation(elementIndex: Int): A? =
+    getElementAnnotations(elementIndex).firstOrNull { it is A } as A?
+
+
+@OptIn(ExperimentalSerializationApi::class)
+internal fun SerialDescriptor.getObjectTags(): ULongArray? {
+    return annotations.filterIsInstance<ObjectTags>().firstOrNull()?.tags
+}
\ No newline at end of file
diff --git a/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/Streams.kt b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/Streams.kt
index 0e5b477..fdbfca6 100644
--- a/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/Streams.kt
+++ b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/Streams.kt
@@ -4,8 +4,6 @@
 
 package kotlinx.serialization.cbor.internal
 
-import kotlinx.serialization.*
-
 internal class ByteArrayInput(private var array: ByteArray) {
     private var position: Int = 0
     public val availableBytes: Int get() = array.size - position
@@ -59,6 +57,10 @@
         return newArray
     }
 
+    fun copyFrom(src: ByteArrayOutput) {
+        write(src.array, count = src.position)
+    }
+
     fun write(buffer: ByteArray, offset: Int = 0, count: Int = buffer.size) {
         // avoid int overflow
         if (offset < 0 || offset > buffer.size || count < 0
diff --git a/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/SuppressAnimalSniffer.kt b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/SuppressAnimalSniffer.kt
new file mode 100644
index 0000000..038b821
--- /dev/null
+++ b/formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/SuppressAnimalSniffer.kt
@@ -0,0 +1,14 @@
+/*
+ * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.cbor.internal
+
+/**
+ * Suppresses Animal Sniffer plugin errors for certain methods.
+ * Such methods include references to Java 8 methods that are not
+ * available in Android API, but can be desugared by R8.
+ */
+@Retention(AnnotationRetention.BINARY)
+@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
+internal annotation class SuppressAnimalSniffer
diff --git a/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborArrayTest.kt b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborArrayTest.kt
new file mode 100644
index 0000000..c0b8595
--- /dev/null
+++ b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborArrayTest.kt
@@ -0,0 +1,179 @@
+package kotlinx.serialization.cbor
+
+import kotlinx.serialization.*
+import kotlin.test.*
+
+
+class CborArrayTest {
+
+    @Test
+    fun writeReadVerifyArraySize1() {
+        /**
+         * 81    # array(1)
+         *    26 # negative(6)
+         */
+        val referenceHexString = "8126"
+        val reference = ClassAs1Array(alg = -7)
+
+        val cbor = Cbor.CoseCompliant
+        assertEquals(referenceHexString, cbor.encodeToHexString(ClassAs1Array.serializer(), reference))
+        assertEquals(reference, cbor.decodeFromHexString(ClassAs1Array.serializer(), referenceHexString))
+    }
+
+    @Test
+    fun writeReadVerifyArraySize2() {
+        /**
+         * C8              # tag(8)
+         *    82           # array(2)
+         *       26        # negative(6)
+         *       63        # text(3)
+         *          666F6F # "foo"
+         */
+        val referenceHexString = "c8822663666f6f"
+        val reference = ClassAs2Array(alg = -7, kid = "foo")
+
+        val cbor = Cbor.CoseCompliant
+        assertEquals(referenceHexString, cbor.encodeToHexString(ClassAs2Array.serializer(), reference))
+        assertEquals(reference, cbor.decodeFromHexString(ClassAs2Array.serializer(), referenceHexString))
+    }
+
+    @Test
+    fun writeReadVerifyArraySize4Nullable() {
+        /**
+         * 84           # array(4)
+         *    26        # negative(6)
+         *    63        # text(3)
+         *       626172 # "bar"
+         *    F6        # primitive(22)
+         *    A0        # map(0)
+         */
+        val referenceHexString = "842663626172f6a0"
+        val reference = ClassAs4ArrayNullable(alg = -7, kid = "bar", iv = null, array = null)
+
+        val cbor = Cbor.CoseCompliant
+
+        assertEquals(referenceHexString, cbor.encodeToHexString(ClassAs4ArrayNullable.serializer(), reference))
+        assertEquals(reference, cbor.decodeFromHexString(ClassAs4ArrayNullable.serializer(), referenceHexString))
+    }
+
+    @Test
+    fun writeReadVerifyClassWithArray() {
+        /**
+         * A1                 # map(1)
+         *    65              # text(5)
+         *       6172726179   # "array"
+         *    C8              # tag(8)
+         *       82           # array(2)
+         *          26        # negative(6)
+         *          63        # text(3)
+         *             626172 # "bar"
+         */
+        val referenceHexString = "a1656172726179c8822663626172"
+        val reference = ClassWithArray(array = ClassAs2Array(alg = -7, kid = "bar"))
+
+        val cbor = Cbor.CoseCompliant
+        assertEquals(referenceHexString, cbor.encodeToHexString(ClassWithArray.serializer(), reference))
+        assertEquals(reference, cbor.decodeFromHexString(ClassWithArray.serializer(), referenceHexString))
+
+        println(
+            cbor.encodeToHexString(
+                DoubleTaggedClassWithArray.serializer(),
+                DoubleTaggedClassWithArray(array = ClassAs2Array(alg = -7, kid = "bar"))
+            )
+        )
+    }
+
+
+    @Test
+    fun writeReadVerifyDoubleTaggedClassWithArray() {
+        /**
+         * A1                    # map(1)
+         *    65                 # text(5)
+         *       6172726179      # "array"
+         *    C9                 # tag(9)
+         *       C8              # tag(8)
+         *          82           # array(2)
+         *             26        # negative(6)
+         *             63        # text(3)
+         *                626172 # "bar"
+         */
+        val referenceHexString = "a1656172726179c9c8822663626172"
+        val reference = DoubleTaggedClassWithArray(array = ClassAs2Array(alg = -7, kid = "bar"))
+
+        val cbor = Cbor.CoseCompliant
+        assertEquals(referenceHexString, cbor.encodeToHexString(DoubleTaggedClassWithArray.serializer(), reference))
+        assertEquals(reference, cbor.decodeFromHexString(DoubleTaggedClassWithArray.serializer(), referenceHexString))
+    }
+
+    @CborArray
+    @Serializable
+    data class ClassAs1Array(
+        @SerialName("alg")
+        val alg: Int,
+    )
+
+    @CborArray
+    @ObjectTags(8U)
+    @Serializable
+    data class ClassAs2Array(
+        @SerialName("alg")
+        val alg: Int,
+        @SerialName("kid")
+        val kid: String,
+    )
+
+    @CborArray
+    @Serializable
+    data class ClassAs4ArrayNullable(
+        @SerialName("alg")
+        val alg: Int,
+        @SerialName("kid")
+        val kid: String,
+        @SerialName("iv")
+        @ByteString
+        val iv: ByteArray?,
+        @SerialName("array")
+        val array: ClassWithArray?
+    ) {
+        override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (other == null || this::class != other::class) return false
+
+            other as ClassAs4ArrayNullable
+
+            if (alg != other.alg) return false
+            if (kid != other.kid) return false
+            if (iv != null) {
+                if (other.iv == null) return false
+                if (!iv.contentEquals(other.iv)) return false
+            } else if (other.iv != null) return false
+            if (array != other.array) return false
+
+            return true
+        }
+
+        override fun hashCode(): Int {
+            var result = alg
+            result = 31 * result + kid.hashCode()
+            result = 31 * result + (iv?.contentHashCode() ?: 0)
+            result = 31 * result + (array?.hashCode() ?: 0)
+            return result
+        }
+    }
+
+
+    @Serializable
+    data class ClassWithArray(
+        @SerialName("array")
+        val array: ClassAs2Array,
+    )
+
+
+    @Serializable
+    data class DoubleTaggedClassWithArray(
+        @ValueTags(9u)
+        @SerialName("array")
+        val array: ClassAs2Array,
+    )
+}
+
diff --git a/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborDecoderTest.kt b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborDecoderTest.kt
new file mode 100644
index 0000000..92aee67
--- /dev/null
+++ b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborDecoderTest.kt
@@ -0,0 +1,377 @@
+/*
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+@file:OptIn(ExperimentalUnsignedTypes::class)
+
+package kotlinx.serialization.cbor
+
+import kotlinx.serialization.*
+import kotlinx.serialization.SimpleSealed.*
+import kotlinx.serialization.cbor.internal.*
+import kotlin.test.*
+
+class CborDecoderTest {
+
+    private val ignoreUnknownKeys = Cbor { ignoreUnknownKeys = true }
+
+    @Test
+    fun testDecodeSimpleObject() {
+        assertEquals(Simple("str"), Cbor.decodeFromHexString(Simple.serializer(), "bf616163737472ff"))
+    }
+
+    @Test
+    fun testDecodeComplicatedObject() {
+        val test = TypesUmbrella(
+            "Hello, world!",
+            42,
+            null,
+            listOf("a", "b"),
+            mapOf(1 to true, 2 to false),
+            Simple("lol"),
+            listOf(Simple("kek")),
+            HexConverter.parseHexBinary("cafe"),
+            HexConverter.parseHexBinary("cafe")
+        )
+        // with maps, lists & strings of indefinite length
+        assertEquals(
+            test, Cbor.decodeFromHexString(
+                TypesUmbrella.serializer(),
+                "bf637374726d48656c6c6f2c20776f726c64216169182a686e756c6c61626c65f6646c6973749f61616162ff636d6170bf01f502f4ff65696e6e6572bf6161636c6f6cff6a696e6e6572734c6973749fbf6161636b656bffff6a62797465537472696e675f42cafeff696279746541727261799f383521ffff"
+            )
+        )
+        // with maps, lists & strings of definite length
+        assertEquals(
+            test, Cbor.decodeFromHexString(
+                TypesUmbrella.serializer(),
+                "a9646c6973748261616162686e756c6c61626c65f6636d6170a202f401f56169182a6a696e6e6572734c69737481a16161636b656b637374726d48656c6c6f2c20776f726c642165696e6e6572a16161636c6f6c6a62797465537472696e6742cafe6962797465417272617982383521"
+            )
+        )
+    }
+
+    @Test
+    fun testReadByteStringWhenNullable() {
+        /* A1                         # map(1)
+         *    6A                      # text(10)
+         *       62797465537472696E67 # "byteString"
+         *    44                      # bytes(4)
+         *       01020304             # "\x01\x02\x03\x04"
+         */
+        assertEquals(
+            expected = NullableByteString(byteArrayOf(1, 2, 3, 4)),
+            actual = Cbor.decodeFromHexString(
+                deserializer = NullableByteString.serializer(),
+                hex = "a16a62797465537472696e674401020304"
+            )
+        )
+
+        /* A1                         # map(1)
+         *    6A                      # text(10)
+         *       62797465537472696E67 # "byteString"
+         *    F6                      # primitive(22)
+         */
+        assertEquals(
+            expected = NullableByteString(byteString = null),
+            actual = Cbor.decodeFromHexString(
+                deserializer = NullableByteString.serializer(),
+                hex = "a16a62797465537472696e67f6"
+            )
+        )
+    }
+
+    @Test
+    fun testNullables() {
+        Cbor.decodeFromHexString<NullableByteStringDefaultNull>("a0")
+    }
+
+    /**
+     * CBOR hex data represents serialized versions of [TypesUmbrella] (which does **not** have a root property 'a') so
+     * decoding to [Simple] (which has the field 'a') is expected to fail.
+     */
+    @Test
+    fun testIgnoreUnknownKeysFailsWhenCborDataIsMissingKeysThatArePresentInKotlinClass() {
+        // with maps & lists of indefinite length
+        assertFailsWithMessage<SerializationException>("Field 'a' is required") {
+            ignoreUnknownKeys.decodeFromHexString(
+                Simple.serializer(),
+                "bf637374726d48656c6c6f2c20776f726c64216169182a686e756c6c61626c65f6646c6973749f61616162ff636d6170bf01f502f4ff65696e6e6572bf6161636c6f6cff6a696e6e6572734c6973749fbf6161636b656bffffff"
+            )
+        }
+
+        // with maps & lists of definite length
+        assertFailsWithMessage<SerializationException>("Field 'a' is required") {
+            ignoreUnknownKeys.decodeFromHexString(
+                Simple.serializer(),
+                "a7646c6973748261616162686e756c6c61626c65f6636d6170a202f401f56169182a6a696e6e6572734c69737481a16161636b656b637374726d48656c6c6f2c20776f726c642165696e6e6572a16161636c6f6c"
+            )
+        }
+    }
+
+    @Test
+    fun testIgnoreUnknownKeysFailsWhenDecodingIncompleteCbor() {
+        /* A3                 # map(3)
+         *    63              # text(3)
+         *       737472       # "str"
+         *    66              # text(6)
+         *       737472696E67 # "string"
+         *    61              # text(1)
+         *       69           # "i"
+         *    00              # unsigned(0)
+         *    66              # text(6)
+         *       69676E6F7265 # "ignore"
+         * (missing value associated with "ignore" key)
+         */
+        assertFailsWithMessage<CborDecodingException>("Unexpected EOF while skipping element") {
+            ignoreUnknownKeys.decodeFromHexString(
+                TypesUmbrella.serializer(),
+                "a36373747266737472696e676169006669676e6f7265"
+            )
+        }
+
+        /* A3                 # map(3)
+         *    63              # text(3)
+         *       737472       # "str"
+         *    66              # text(6)
+         *       737472696E67 # "string"
+         *    61              # text(1)
+         *       69           # "i"
+         *    00              # unsigned(0)
+         *    66              # text(6)
+         *       69676E6F7265 # "ignore"
+         *    A2              # map(2)
+         * (missing map contents associated with "ignore" key)
+         */
+        assertFailsWithMessage<CborDecodingException>("Unexpected EOF while skipping element") {
+            ignoreUnknownKeys.decodeFromHexString(
+                TypesUmbrella.serializer(),
+                "a36373747266737472696e676169006669676e6f7265a2"
+            )
+        }
+    }
+
+    @Test
+    fun testIgnoreUnknownKeysFailsWhenEncounteringPreemptiveBreak() {
+        /* A3                 # map(3)
+         *    63              # text(3)
+         *       737472       # "str"
+         *    66              # text(6)
+         *       737472696E67 # "string"
+         *    66              # text(6)
+         *       69676E6F7265 # "ignore"
+         *    FF              # primitive(*)
+         */
+        assertFailsWithMessage<CborDecodingException>("Expected next data item, but found FF") {
+            ignoreUnknownKeys.decodeFromHexString(
+                TypesUmbrella.serializer(),
+                "a36373747266737472696e676669676e6f7265ff"
+            )
+        }
+    }
+
+
+    @Test
+    fun testDecodeCborWithUnknownField() {
+        assertEquals(
+            expected = Simple("123"),
+            actual = ignoreUnknownKeys.decodeFromHexString(
+                deserializer = Simple.serializer(),
+
+                /* BF           # map(*)
+                 *    61        # text(1)
+                 *       61     # "a"
+                 *    63        # text(3)
+                 *       313233 # "123"
+                 *    61        # text(1)
+                 *       62     # "b"
+                 *    63        # text(3)
+                 *       393837 # "987"
+                 *    FF        # primitive(*)
+                 */
+                hex = "bf616163313233616263393837ff"
+            )
+        )
+    }
+
+    @Test
+    fun testDecodeCborWithUnknownNestedIndefiniteFields() {
+        assertEquals(
+            expected = Simple("123"),
+            actual = ignoreUnknownKeys.decodeFromHexString(
+                deserializer = Simple.serializer(),
+
+                /* BF             # map(*)
+                 *    61          # text(1)
+                 *       61       # "a"
+                 *    63          # text(3)
+                 *       313233   # "123"
+                 *    61          # text(1)
+                 *       62       # "b"
+                 *    BF          # map(*)
+                 *       7F       # text(*)
+                 *          61    # text(1)
+                 *             78 # "x"
+                 *          FF    # primitive(*)
+                 *       A1       # map(1)
+                 *          61    # text(1)
+                 *             79 # "y"
+                 *          0A    # unsigned(10)
+                 *       FF       # primitive(*)
+                 *    61          # text(1)
+                 *       63       # "c"
+                 *    9F          # array(*)
+                 *       01       # unsigned(1)
+                 *       02       # unsigned(2)
+                 *       03       # unsigned(3)
+                 *       FF       # primitive(*)
+                 *    FF          # primitive(*)
+                 */
+                hex = "bf6161633132336162bf7f6178ffa161790aff61639f010203ffff"
+            )
+        )
+    }
+
+    /**
+     * The following CBOR diagnostic output demonstrates the additional fields (prefixed with `+` in front of each line)
+     * present in the encoded CBOR data that does not have associated fields in the Kotlin classes (they will be skipped
+     * over with `ignoreUnknownKeys` is enabled).
+     *
+     * ```diff
+     *   {
+     * +   "extra": [
+     * +     9,
+     * +     8,
+     * +     7
+     * +   ],
+     *     "boxed": [
+     *       [
+     *         "kotlinx.serialization.SimpleSealed.SubSealedA",
+     *         {
+     *           "s": "a",
+     * +         "newA": {
+     * +           "x": 1,
+     * +           "y": 2
+     * +         }
+     *         }
+     *       ],
+     *       [
+     *         "kotlinx.serialization.SimpleSealed.SubSealedB",
+     *         {
+     *           "i": 1
+     *         }
+     *       ]
+     *     ]
+     *   }
+     * ```
+     */
+    @Test
+    fun testDecodeCborWithUnknownKeysInSealedClasses() {
+        /* BF                      # map(*)
+         *    65                   # text(5)
+         *       6578747261        # "extra"
+         *    83                   # array(3)
+         *       09                # unsigned(9)
+         *       08                # unsigned(8)
+         *       07                # unsigned(7)
+         *    65                   # text(5)
+         *       626F786564        # "boxed"
+         *    9F                   # array(*)
+         *       9F                # array(*)
+         *          78 2D          # text(45)
+         *             6B6F746C696E782E73657269616C697A6174696F6E2E53696D706C655365616C65642E5375625365616C656441 # "kotlinx.serialization.SimpleSealed.SubSealedA"
+         *          BF             # map(*)
+         *             61          # text(1)
+         *                73       # "s"
+         *             61          # text(1)
+         *                61       # "a"
+         *             64          # text(4)
+         *                6E657741 # "newA"
+         *             BF          # map(*)
+         *                61       # text(1)
+         *                   78    # "x"
+         *                01       # unsigned(1)
+         *                61       # text(1)
+         *                   79    # "y"
+         *                02       # unsigned(2)
+         *                FF       # primitive(*)
+         *             FF          # primitive(*)
+         *          FF             # primitive(*)
+         *       9F                # array(*)
+         *          78 2D          # text(45)
+         *             6B6F746C696E782E73657269616C697A6174696F6E2E53696D706C655365616C65642E5375625365616C656442 # "kotlinx.serialization.SimpleSealed.SubSealedB"
+         *          BF             # map(*)
+         *             61          # text(1)
+         *                69       # "i"
+         *             01          # unsigned(1)
+         *             FF          # primitive(*)
+         *          FF             # primitive(*)
+         *       FF                # primitive(*)
+         *    FF                   # primitive(*)
+         */
+
+        assertEquals(
+            expected = SealedBox(
+                listOf(
+                    SubSealedA("a"),
+                    SubSealedB(1)
+                )
+            ),
+            actual = ignoreUnknownKeys.decodeFromHexString(
+                SealedBox.serializer(),
+                "bf6565787472618309080765626f7865649f9f782d6b6f746c696e782e73657269616c697a6174696f6e2e53696d706c655365616c65642e5375625365616c656441bf61736161646e657741bf617801617902ffffff9f782d6b6f746c696e782e73657269616c697a6174696f6e2e53696d706c655365616c65642e5375625365616c656442bf616901ffffffff"
+            )
+        )
+    }
+
+    @Test
+    fun testReadCustomByteString() {
+        assertEquals(
+            expected = TypeWithCustomByteString(CustomByteString(0x11, 0x22, 0x33)),
+            actual = Cbor.decodeFromHexString("bf617843112233ff")
+        )
+    }
+
+    @Test
+    fun testReadNullableCustomByteString() {
+        assertEquals(
+            expected = TypeWithNullableCustomByteString(CustomByteString(0x11, 0x22, 0x33)),
+            actual = Cbor.decodeFromHexString("bf617843112233ff")
+        )
+    }
+
+    @Test
+    fun testReadNullCustomByteString() {
+        assertEquals(
+            expected = TypeWithNullableCustomByteString(null),
+            actual = Cbor.decodeFromHexString("bf6178f6ff")
+        )
+    }
+
+    @Test
+    fun testReadValueClassWithByteString() {
+        assertContentEquals(
+            expected = byteArrayOf(0x11, 0x22, 0x33),
+            actual = Cbor.decodeFromHexString<ValueClassWithByteString>("43112233").x
+        )
+    }
+
+    @Test
+    fun testReadValueClassCustomByteString() {
+        assertEquals(
+            expected = ValueClassWithCustomByteString(CustomByteString(0x11, 0x22, 0x33)),
+            actual = Cbor.decodeFromHexString("43112233")
+        )
+    }
+
+    @Test
+    fun testReadValueClassWithUnlabeledByteString() {
+        assertContentEquals(
+            expected = byteArrayOf(
+                0x11,
+                0x22,
+                0x33
+            ),
+            actual = Cbor.decodeFromHexString<ValueClassWithUnlabeledByteString>("43112233").x.x
+        )
+    }
+
+}
diff --git a/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborDefiniteLengthTest.kt b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborDefiniteLengthTest.kt
new file mode 100644
index 0000000..b19a409
--- /dev/null
+++ b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborDefiniteLengthTest.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.cbor
+
+import kotlinx.serialization.*
+import kotlin.test.*
+
+
+class CborDefiniteLengthTest {
+    @Test
+    fun writeComplicatedClass() {
+        val test = TypesUmbrella(
+            "Hello, world!",
+            42,
+            null,
+            listOf("a", "b"),
+            mapOf(1 to true, 2 to false),
+            Simple("lol"),
+            listOf(Simple("kek")),
+            HexConverter.parseHexBinary("cafe"),
+            HexConverter.parseHexBinary("cafe")
+        )
+        assertEquals(
+            "a9637374726d48656c6c6f2c20776f726c64216169182a686e756c6c61626c65f6646c6973748261616162636d6170a201f502f465696e6e6572a16161636c6f6c6a696e6e6572734c69737481a16161636b656b6a62797465537472696e6742cafe6962797465417272617982383521",
+            Cbor { useDefiniteLengthEncoding = true }.encodeToHexString(TypesUmbrella.serializer(), test)
+        )
+    }
+
+}
\ No newline at end of file
diff --git a/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborIsoTest.kt b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborIsoTest.kt
new file mode 100644
index 0000000..49ef339
--- /dev/null
+++ b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborIsoTest.kt
@@ -0,0 +1,50 @@
+package kotlinx.serialization.cbor
+
+import kotlinx.serialization.*
+import kotlin.test.*
+
+class CborIsoTest {
+
+    private val reference = DataClass(
+        bytes = "foo".encodeToByteArray()
+    )
+
+    /**
+     * A1               # map(1)
+     *    65            # text(5)
+     *       6279746573 # "bytes"
+     *    43            # bytes(3)
+     *       666F6F     # "foo"
+     *
+     */
+    private val referenceHexString = "a165627974657343666f6f"
+
+    @Test
+    fun writeReadVerifyCoseSigned() {
+        val cbor = Cbor {
+            alwaysUseByteString = true
+            useDefiniteLengthEncoding = true
+        }
+        assertEquals(reference, cbor.decodeFromHexString(DataClass.serializer(), referenceHexString))
+        assertEquals(referenceHexString, cbor.encodeToHexString(DataClass.serializer(), reference))
+    }
+
+    @Serializable
+    data class DataClass(
+        @SerialName("bytes")
+        val bytes: ByteArray,
+    ) {
+        override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (other == null || this::class != other::class) return false
+
+            other as DataClass
+
+            return bytes.contentEquals(other.bytes)
+        }
+
+        override fun hashCode(): Int {
+            return bytes.contentHashCode()
+        }
+    }
+}
\ No newline at end of file
diff --git a/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborLabelTest.kt b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborLabelTest.kt
new file mode 100644
index 0000000..0ecb528
--- /dev/null
+++ b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborLabelTest.kt
@@ -0,0 +1,155 @@
+package kotlinx.serialization.cbor
+
+import kotlinx.serialization.*
+import kotlinx.serialization.cbor.internal.CborDecodingException
+import kotlin.test.*
+
+
+class CborLabelTest {
+
+    private val reference = ClassWithCborLabel(alg = -7)
+
+
+    /**
+     * BF    # map(*)
+     *    01 # unsigned(1)
+     *    26 # negative(6)
+     *    FF # primitive(*)
+     */
+    private val referenceHexLabelString = "bf0126ff"
+
+    /**
+     * BF           # map(*)
+     *    63        # text(3)
+     *       616C67 # "alg"
+     *    26        # negative(6)
+     *    FF        # primitive(*)
+     */
+    private val referenceHexNameString = "bf63616c6726ff"
+
+
+    @Test
+    fun writeReadVerifyCborLabel() {
+        val cbor = Cbor {
+            preferCborLabelsOverNames = true
+        }
+        assertEquals(referenceHexLabelString, cbor.encodeToHexString(ClassWithCborLabel.serializer(), reference))
+        assertEquals(reference, cbor.decodeFromHexString(ClassWithCborLabel.serializer(), referenceHexLabelString))
+    }
+
+    @Test
+    fun writeReadVerifySerialName() {
+        val cbor = Cbor {
+            preferCborLabelsOverNames = false
+        }
+        assertEquals(referenceHexNameString, cbor.encodeToHexString(ClassWithCborLabel.serializer(), reference))
+        assertEquals(reference, cbor.decodeFromHexString(ClassWithCborLabel.serializer(), referenceHexNameString))
+    }
+
+    @Test
+    fun writeReadVerifyCborLabelWithTags() {
+        val referenceWithTag = ClassWithCborLabelAndTag(alg = -7)
+        /**
+         * A1       # map(1)
+         *    C5    # tag(5)
+         *       01 # unsigned(1)
+         *    26    # negative(6)
+         */
+        val referenceHexLabelWithTagString = "a1c50126"
+        val cbor = Cbor {
+            preferCborLabelsOverNames = true
+            encodeKeyTags = true
+            verifyKeyTags = true
+            useDefiniteLengthEncoding = true
+        }
+        assertEquals(referenceHexLabelWithTagString, cbor.encodeToHexString(ClassWithCborLabelAndTag.serializer(), referenceWithTag))
+        assertEquals(referenceWithTag, cbor.decodeFromHexString(ClassWithCborLabelAndTag.serializer(), referenceHexLabelWithTagString))
+    }
+
+    @Test
+    fun writeReadVerifyCborLabelWithTagsThrowing() {
+        /**
+         * A1       # map(1)
+         *    C6    # tag(6)        // wrong tag: declared is 5U, meaning C5 in hex
+         *       01 # unsigned(1)
+         *    26    # negative(6)
+         */
+        val referenceHexLabelWithTagString = "a1c60126"
+        val cbor = Cbor {
+            preferCborLabelsOverNames = true
+            encodeKeyTags = true
+            verifyKeyTags = true
+            useDefiniteLengthEncoding = true
+        }
+        assertFailsWith(CborDecodingException::class) {
+            cbor.decodeFromHexString(ClassWithCborLabelAndTag.serializer(), referenceHexLabelWithTagString)
+        }
+    }
+
+    @Test
+    fun writeReadVerifyCborLabelWithTagsAndUnknownKeys() {
+        val referenceWithTag = ClassWithCborLabelAndTag(alg = -7)
+        /**
+         * A2           # map(2)
+         *    C5        # tag(5)
+         *       01     # unsigned(1)
+         *    26        # negative(6)
+         *    02        # unsigned(2)
+         *    63        # text(3)
+         *       62617A # "baz"
+         */
+        val referenceHexLabelWithTagString = "a2c50126026362617a"
+        val cbor = Cbor {
+            preferCborLabelsOverNames = true
+            encodeKeyTags = true
+            verifyKeyTags = true
+            ignoreUnknownKeys = true
+            useDefiniteLengthEncoding = true
+        }
+        assertEquals(referenceWithTag, cbor.decodeFromHexString(ClassWithCborLabelAndTag.serializer(), referenceHexLabelWithTagString))
+    }
+
+    @Test
+    fun writeClassWithoutLabelBuPreferLabel() {
+
+        //only serialName is present, no label, so fallback to serialName
+        val referenceWithoutLabel = ClassWithoutCborLabel(algorithm = 9)
+        /**
+         * BF           # map(*)
+         *    63        # text(3)
+         *       616C67 # "alg"
+         *    09        # unsigned(9)
+         *    FF        # primitive(*)
+         */
+
+        val referenceHexStringWithoutLabel = "bf63616c6709ff"
+        val cbor = Cbor {
+            preferCborLabelsOverNames = true
+        }
+
+        assertEquals(referenceWithoutLabel, cbor.decodeFromHexString(ClassWithoutCborLabel.serializer(), referenceHexStringWithoutLabel))
+    }
+
+    @Serializable
+    data class ClassWithCborLabel(
+        @CborLabel(1)
+        @SerialName("alg")
+        val alg: Int
+    )
+
+    @Serializable
+    data class ClassWithCborLabelAndTag(
+        @CborLabel(1)
+        @SerialName("alg")
+        @KeyTags(5U)
+        val alg: Int
+    )
+
+    @Serializable
+    data class ClassWithoutCborLabel(
+        @SerialName("alg")
+        val algorithm: Int
+    )
+
+}
+
diff --git a/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborParserTest.kt b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborParserTest.kt
new file mode 100644
index 0000000..d8c7c69
--- /dev/null
+++ b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborParserTest.kt
@@ -0,0 +1,565 @@
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+@file:OptIn(ExperimentalUnsignedTypes::class)
+
+package kotlinx.serialization.cbor
+
+import kotlinx.serialization.*
+import kotlinx.serialization.SimpleSealed.*
+import kotlinx.serialization.cbor.internal.*
+import kotlin.test.*
+
+class CborParserTest {
+
+    private fun withParser(input: String, block: CborParser.() -> Unit) {
+        val bytes = HexConverter.parseHexBinary(input.uppercase())
+        CborParser(ByteArrayInput(bytes), false).block()
+    }
+
+    @Test
+    fun testParseIntegers() {
+        withParser("0C1903E8") {
+            assertEquals(12L, nextNumber())
+            assertEquals(1000L, nextNumber())
+        }
+        withParser("203903e7") {
+            assertEquals(-1L, nextNumber())
+            assertEquals(-1000L, nextNumber())
+        }
+    }
+
+    @Test
+    fun testParseStrings() {
+        withParser("6568656C6C6F") {
+            assertEquals("hello", nextString())
+        }
+        withParser("7828737472696E672074686174206973206C6F6E676572207468616E2032332063686172616374657273") {
+            assertEquals("string that is longer than 23 characters", nextString())
+        }
+    }
+
+    @Test
+    fun testParseDoubles() {
+        withParser("fb7e37e43c8800759c") {
+            assertEquals(1e+300, nextDouble())
+        }
+        withParser("fa47c35000") {
+            assertEquals(100000.0f, nextFloat())
+        }
+    }
+
+
+    /**
+     * Test using example shown on page 11 of [RFC 7049 2.2.2](https://tools.ietf.org/html/rfc7049#section-2.2.2):
+     *
+     * ```
+     * 0b010_11111 0b010_00100 0xaabbccdd 0b010_00011 0xeeff99 0b111_11111
+     *
+     * 5F              -- Start indefinite-length byte string
+     *    44           -- Byte string of length 4
+     *       aabbccdd  -- Bytes content
+     *    43           -- Byte string of length 3
+     *       eeff99    -- Bytes content
+     *    FF           -- "break"
+     *
+     * After decoding, this results in a single byte string with seven
+     * bytes: 0xaabbccddeeff99.
+     * ```
+     */
+    @Test
+    fun testRfc7049IndefiniteByteStringExample() {
+        withParser(input = "5F44aabbccdd43eeff99FF") {
+            assertEquals(
+                expected = "aabbccddeeff99",
+                actual = HexConverter.printHexBinary(nextByteString(), lowerCase = true)
+            )
+        }
+    }
+
+
+    /**
+     * Tests skipping unknown keys associated with values of the following CBOR types:
+     * - Major type 0: an unsigned integer
+     * - Major type 1: a negative integer
+     * - Major type 2: a byte string
+     * - Major type 3: a text string
+     */
+    @Test
+    fun testSkipPrimitives() {
+        /* A4                           # map(4)
+         *    61                        # text(1)
+         *       61                     # "a"
+         *    1B FFFFFFFFFFFFFFFF       # unsigned(18446744073709551615)
+         *    61                        # text(1)
+         *       62                     # "b"
+         *    20                        # negative(0)
+         *    61                        # text(1)
+         *       63                     # "c"
+         *    42                        # bytes(2)
+         *       CAFE                   # "\xCA\xFE"
+         *    61                        # text(1)
+         *       64                     # "d"
+         *    6B                        # text(11)
+         *       48656C6C6F20776F726C64 # "Hello world"
+         */
+        withParser("a461611bffffffffffffffff616220616342cafe61646b48656c6c6f20776f726c64") {
+            expectMap(size = 4)
+            expect("a")
+            skipElement() // unsigned(18446744073709551615)
+            expect("b")
+            skipElement() // negative(0)
+            expect("c")
+            skipElement() // "\xCA\xFE"
+            expect("d")
+            skipElement() // "Hello world"
+            expectEof()
+        }
+    }
+
+    /**
+     * Tests skipping unknown keys associated with values (that are empty) of the following CBOR types:
+     * - Major type 2: a byte string
+     * - Major type 3: a text string
+     */
+    @Test
+    fun testSkipEmptyPrimitives() {
+        /* A2       # map(2)
+         *    61    # text(1)
+         *       61 # "a"
+         *    40    # bytes(0)
+         *          # ""
+         *    61    # text(1)
+         *       62 # "b"
+         *    60    # text(0)
+         *          # ""
+         */
+        withParser("a2616140616260") {
+            expectMap(size = 2)
+            expect("a")
+            skipElement() // bytes(0)
+            expect("b")
+            skipElement() // text(0)
+            expectEof()
+        }
+    }
+
+    /**
+     * Tests skipping unknown keys associated with values of the following CBOR types:
+     * - Major type 4: an array of data items
+     * - Major type 5: a map of pairs of data items
+     */
+    @Test
+    fun testSkipCollections() {
+        /* A2                                  # map(2)
+         *    61                               # text(1)
+         *       61                            # "a"
+         *    83                               # array(3)
+         *       01                            # unsigned(1)
+         *       18 FF                         # unsigned(255)
+         *       1A 00010000                   # unsigned(65536)
+         *    61                               # text(1)
+         *       62                            # "b"
+         *    A2                               # map(2)
+         *       61                            # text(1)
+         *          78                         # "x"
+         *       67                            # text(7)
+         *          6B6F746C696E78             # "kotlinx"
+         *       61                            # text(1)
+         *          79                         # "y"
+         *       6D                            # text(13)
+         *          73657269616C697A6174696F6E # "serialization"
+         */
+        withParser("a26161830118ff1a000100006162a26178676b6f746c696e7861796d73657269616c697a6174696f6e") {
+            expectMap(size = 2)
+            expect("a")
+            skipElement() // [1, 255, 65536]
+            expect("b")
+            skipElement() // {"x": "kotlinx", "y": "serialization"}
+            expectEof()
+        }
+    }
+
+    /**
+     * Tests skipping unknown keys associated with values (empty collections) of the following CBOR types:
+     * - Major type 4: an array of data items
+     * - Major type 5: a map of pairs of data items
+     */
+    @Test
+    fun testSkipEmptyCollections() {
+        /* A2       # map(2)
+         *    61    # text(1)
+         *       61 # "a"
+         *    80    # array(0)
+         *    61    # text(1)
+         *       62 # "b"
+         *    A0    # map(0)
+         */
+        withParser("a26161806162a0") {
+            expectMap(size = 2)
+            expect("a")
+            skipElement() // [1, 255, 65536]
+            expect("b")
+            skipElement() // {"x": "kotlinx", "y": "serialization"}
+            expectEof()
+        }
+    }
+
+    /**
+     * Tests skipping unknown keys associated with **indefinite length** values of the following CBOR types:
+     * - Major type 2: a byte string
+     * - Major type 3: a text string
+     * - Major type 4: an array of data items
+     * - Major type 5: a map of pairs of data items
+     */
+    @Test
+    fun testSkipIndefiniteLength() {
+        /* A4                                  # map(4)
+         *    61                               # text(1)
+         *       61                            # "a"
+         *    5F                               # bytes(*)
+         *       42                            # bytes(2)
+         *          CAFE                       # "\xCA\xFE"
+         *       43                            # bytes(3)
+         *          010203                     # "\x01\x02\x03"
+         *       FF                            # primitive(*)
+         *    61                               # text(1)
+         *       62                            # "b"
+         *    7F                               # text(*)
+         *       66                            # text(6)
+         *          48656C6C6F20               # "Hello "
+         *       65                            # text(5)
+         *          776F726C64                 # "world"
+         *       FF                            # primitive(*)
+         *    61                               # text(1)
+         *       63                            # "c"
+         *    9F                               # array(*)
+         *       67                            # text(7)
+         *          6B6F746C696E78             # "kotlinx"
+         *       6D                            # text(13)
+         *          73657269616C697A6174696F6E # "serialization"
+         *       FF                            # primitive(*)
+         *    61                               # text(1)
+         *       64                            # "d"
+         *    BF                               # map(*)
+         *       61                            # text(1)
+         *          31                         # "1"
+         *       01                            # unsigned(1)
+         *       61                            # text(1)
+         *          32                         # "2"
+         *       02                            # unsigned(2)
+         *       61                            # text(1)
+         *          33                         # "3"
+         *       03                            # unsigned(3)
+         *       FF                            # primitive(*)
+         */
+        withParser("a461615f42cafe43010203ff61627f6648656c6c6f2065776f726c64ff61639f676b6f746c696e786d73657269616c697a6174696f6eff6164bf613101613202613303ff") {
+            expectMap(size = 4)
+            expect("a")
+            skipElement() // "\xCA\xFE\x01\x02\x03"
+            expect("b")
+            skipElement() // "Hello world"
+            expect("c")
+            skipElement() // ["kotlinx", "serialization"]
+            expect("d")
+            skipElement() // {"1": 1, "2": 2, "3": 3}
+            expectEof()
+        }
+    }
+
+    /**
+     * Tests that skipping unknown keys also skips over associated tags.
+     *
+     * Includes tags on the key, tags on the value, and tags on both key and value.
+     */
+    @Test
+    fun testSkipTags() {
+        /*
+         * A4                                 # map(4)
+         * 61                              # text(1)
+         *    61                           # "a"
+         * CC                              # tag(12)
+         *    1B FFFFFFFFFFFFFFFF          # unsigned(18446744073709551615)
+         * D8 22                           # tag(34)
+         *    61                           # text(1)
+         *       62                        # "b"
+         * 20                              # negative(0)
+         * D8 38                           # tag(56)
+         *    61                           # text(1)
+         *       63                        # "c"
+         * D8 4E                           # tag(78)
+         *    42                           # bytes(2)
+         *       CAFE                      # "\xCA\xFE"
+         * 61                              # text(1)
+         *    64                           # "d"
+         * D8 5A                           # tag(90)
+         *    CC                           # tag(12)
+         *       6B                        # text(11)
+         *          48656C6C6F20776F726C64 # "Hello world"
+         */
+        withParser("A46161CC1BFFFFFFFFFFFFFFFFD822616220D8386163D84E42CAFE6164D85ACC6B48656C6C6F20776F726C64") {
+            expectMap(size = 4)
+            expect("a")
+            skipElement() // unsigned(18446744073709551615)
+            expect("b")
+            skipElement() // negative(0)
+            expect("c")
+            skipElement() // "\xCA\xFE"
+            expect("d")
+            skipElement() // "Hello world"
+            expectEof()
+        }
+    }
+
+    /**
+     * Tests that skipping unknown keys also skips over associated tags.
+     *
+     * Includes tags on the key, tags on the value, and tags on both key and value.
+     */
+    @Test
+    fun testVerifyTags() {
+        /*
+         * A4                                 # map(4)
+         * 61                              # text(1)
+         *    61                           # "a"
+         * CC                              # tag(12)
+         *    1B FFFFFFFFFFFFFFFF          # unsigned(18446744073709551615)
+         * D8 22                           # tag(34)
+         *    61                           # text(1)
+         *       62                        # "b"
+         * 20                              # negative(0)
+         * D8 38                           # tag(56)
+         *    61                           # text(1)
+         *       63                        # "c"
+         * D8 4E                           # tag(78)
+         *    42                           # bytes(2)
+         *       CAFE                      # "\xCA\xFE"
+         * 61                              # text(1)
+         *    64                           # "d"
+         * D8 5A                           # tag(90)
+         *    CC                           # tag(12)
+         *       6B                        # text(11)
+         *          48656C6C6F20776F726C64 # "Hello world"
+         */
+        withParser("A46161CC1BFFFFFFFFFFFFFFFFD822616220D8386163D84E42CAFE6164D85ACC6B48656C6C6F20776F726C64") {
+            expectMap(size = 4)
+            expect("a")
+            skipElement(12uL) // unsigned(18446744073709551615)
+            expect("b", 34uL)
+            skipElement(null) // negative(0); explicitly setting parameter to null for clearer semantics
+            expect("c", 56uL)
+            skipElement(78uL) // "\xCA\xFE"
+            expect("d")
+            skipElement(ulongArrayOf(90uL, 12uL)) // "Hello world"
+            expectEof()
+        }
+    }
+
+    @Test
+    fun testIgnoresTagsOnStrings() {
+        /*
+         * 84                                # array(4)
+         * 68                             # text(8)
+         *    756E746167676564            # "untagged"
+         * C0                             # tag(0)
+         *    68                          # text(8)
+         *       7461676765642D30         # "tagged-0"
+         * D8 F5                          # tag(245)
+         *    6A                          # text(10)
+         *       7461676765642D323435     # "tagged-244"
+         * D9 3039                        # tag(12345)
+         *    6C                          # text(12)
+         *       7461676765642D3132333435 # "tagged-12345"
+         *
+         */
+        withParser("8468756E746167676564C0687461676765642D30D8F56A7461676765642D323435D930396C7461676765642D3132333435") {
+            assertEquals(4, startArray())
+            assertEquals("untagged", nextString())
+            assertEquals("tagged-0", nextString())
+            assertEquals("tagged-245", nextString())
+            assertEquals("tagged-12345", nextString())
+        }
+    }
+
+    @Test
+    fun testVerifyTagsOnStrings() {
+        /*
+         * 84                             # array(4)
+         * 68                             # text(8)
+         *    756E746167676564            # "untagged"
+         * C0                             # tag(0)
+         *    68                          # text(8)
+         *       7461676765642D30         # "tagged-0"
+         * D8 F5                          # tag(245)
+         *    6A                          # text(10)
+         *       7461676765642D323435     # "tagged-244"
+         * D9 3039                        # tag(12345)
+         *    6C                          # text(12)
+         *       7461676765642D3132333435 # "tagged-12345"
+         *
+         */
+        withParser("8468756E746167676564C0687461676765642D30D8F56A7461676765642D323435D930396C7461676765642D3132333435") {
+            assertEquals(4, startArray(null))
+            assertEquals("untagged", nextString(null))
+            assertEquals("tagged-0", nextString(0u))
+            assertEquals("tagged-245", nextString(245uL))
+            assertEquals("tagged-12345", nextString(12345uL))
+        }
+    }
+
+    @Test
+    fun testIgnoresTagsOnNumbers() {
+        /*
+         * 86                     # array(6)
+         * 18 7B                  # unsigned(123)
+         * C0                     # tag(0)
+         *    1A 0001E240         # unsigned(123456)
+         * D8 F5                  # tag(245)
+         *    1A 000F423F         # unsigned(999999)
+         * D9 3039                # tag(12345)
+         *    38 31               # negative(49)
+         * D8 22                  # tag(34)
+         *    FB 3FE161F9F01B866E # primitive(4603068020252444270)
+         * D9 0237                # tag(567)
+         *    FB 401999999999999A # primitive(4618891777831180698)
+         */
+        withParser("86187BC01A0001E240D8F51A000F423FD930393831D822FB3FE161F9F01B866ED90237FB401999999999999A") {
+            assertEquals(6, startArray())
+            assertEquals(123, nextNumber())
+            assertEquals(123456, nextNumber())
+            assertEquals(999999, nextNumber())
+            assertEquals(-50, nextNumber())
+            assertEquals(0.54321, nextDouble(), 0.00001)
+            assertEquals(6.4, nextDouble(), 0.00001)
+        }
+    }
+
+    @Test
+    fun testVerifiesTagsOnNumbers() {
+        /*
+         * 86                     # array(6)
+         * 18 7B                  # unsigned(123)
+         * C0                     # tag(0)
+         *    1A 0001E240         # unsigned(123456)
+         * D8 F5                  # tag(245)
+         *    1A 000F423F         # unsigned(999999)
+         * D9 3039                # tag(12345)
+         *    38 31               # negative(49)
+         * D8 22                  # tag(34)
+         *    FB 3FE161F9F01B866E # primitive(4603068020252444270)
+         * D9 0237                # tag(567)
+         *    FB 401999999999999A # primitive(4618891777831180698)
+         */
+        withParser("86187BC01A0001E240D8F51A000F423FD930393831D822FB3FE161F9F01B866ED90237FB401999999999999A") {
+            assertEquals(6, startArray(null))
+            assertEquals(123, nextNumber(null))
+            assertEquals(123456, nextNumber(0uL))
+            assertEquals(999999, nextNumber(245uL))
+            assertEquals(-50, nextNumber(12345uL))
+            assertEquals(0.54321, nextDouble(34uL), 0.00001)
+            assertEquals(6.4, nextDouble(567uL), 0.00001)
+        }
+    }
+
+    @Test
+    fun testIgnoresTagsOnArraysAndMaps() {
+        /*
+         * A2                                  # map(2)
+         * 63                                  # text(3)
+         *    6D6170                           # "map"
+         * D8 7B                               # tag(123)
+         *    A1                               # map(1)
+         *       68                            # text(8)
+         *          74686973206D6170           # "this map"
+         *       6D                            # text(13)
+         *          69732074616767656420313233 # "is tagged 123"
+         * 65                                  # text(5)
+         *    6172726179                       # "array"
+         * DA 0012D687                         # tag(1234567)
+         *    83                               # array(3)
+         *       6A                            # text(10)
+         *          74686973206172726179       # "this array"
+         *       69                            # text(9)
+         *          697320746167676564         # "is tagged"
+         *       67                            # text(7)
+         *          31323334353637             # "1234567"
+         */
+        withParser("A2636D6170D87BA16874686973206D61706D69732074616767656420313233656172726179DA0012D687836A74686973206172726179696973207461676765646731323334353637") {
+            assertEquals(2, startMap())
+            assertEquals("map", nextString())
+            assertEquals(1, startMap())
+            assertEquals("this map", nextString())
+            assertEquals("is tagged 123", nextString())
+            assertEquals("array", nextString())
+            assertEquals(3, startArray())
+            assertEquals("this array", nextString())
+            assertEquals("is tagged", nextString())
+            assertEquals("1234567", nextString())
+        }
+    }
+
+    @Test
+    fun testVerifiesTagsOnArraysAndMaps() {
+        /*
+         * A2                                  # map(2)
+         * 63                                  # text(3)
+         *    6D6170                           # "map"
+         * D8 7B                               # tag(123)
+         *    A1                               # map(1)
+         *       68                            # text(8)
+         *          74686973206D6170           # "this map"
+         *       6D                            # text(13)
+         *          69732074616767656420313233 # "is tagged 123"
+         * 65                                  # text(5)
+         *    6172726179                       # "array"
+         * DA 0012D687                         # tag(1234567)
+         *    83                               # array(3)
+         *       6A                            # text(10)
+         *          74686973206172726179       # "this array"
+         *       69                            # text(9)
+         *          697320746167676564         # "is tagged"
+         *       67                            # text(7)
+         *          31323334353637             # "1234567"
+         */
+        withParser("A2636D6170D87BA16874686973206D61706D69732074616767656420313233656172726179DA0012D687836A74686973206172726179696973207461676765646731323334353637") {
+            assertEquals(2, startMap(null))
+            assertEquals("map", nextString(null))
+            assertEquals(1, startMap(123uL))
+            assertEquals("this map", nextString(null))
+            assertEquals("is tagged 123", nextString(null))
+            assertEquals("array", nextString(null))
+            assertEquals(3, startArray(1234567uL))
+            assertEquals("this array", nextString(null))
+            assertEquals("is tagged", nextString(null))
+            assertEquals("1234567", nextString(null))
+        }
+    }
+}
+
+
+private fun CborParser.nextNumber(tag: ULong): Long = nextNumber(ulongArrayOf(tag))
+
+private fun CborParser.nextDouble(tag: ULong) = nextDouble(ulongArrayOf(tag))
+
+private fun CborParser.nextString(tag: ULong) = nextString(ulongArrayOf(tag))
+
+private fun CborParser.startArray(tag: ULong): Int = startArray(ulongArrayOf(tag))
+
+private fun CborParser.startMap(tag: ULong) = startMap(ulongArrayOf(tag))
+
+private fun CborParser.skipElement(singleTag: ULong) = skipElement(ulongArrayOf(singleTag))
+
+private fun CborParser.skipElement() = skipElement(null)
+
+private fun CborParser.expect(expected: String, tag: ULong? = null) {
+    assertEquals(expected, actual = nextString(tag?.let { ulongArrayOf(it) }), "string")
+}
+
+private fun CborParser.expectMap(size: Int, tag: ULong? = null) {
+    assertEquals(size, actual = startMap(tag?.let { ulongArrayOf(it) }), "map size")
+}
+
+private fun CborParser.expectEof() {
+    assertTrue(isEof(), "Expected EOF.")
+}
diff --git a/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborReaderTest.kt b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborReaderTest.kt
deleted file mode 100644
index f615d5e..0000000
--- a/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborReaderTest.kt
+++ /dev/null
@@ -1,765 +0,0 @@
-/*
- * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-package kotlinx.serialization.cbor
-
-import kotlinx.serialization.*
-import kotlinx.serialization.SimpleSealed.*
-import kotlinx.serialization.cbor.internal.*
-import kotlin.test.*
-
-class CborReaderTest {
-
-    private val ignoreUnknownKeys = Cbor { ignoreUnknownKeys = true }
-
-    private fun withDecoder(input: String, block: CborDecoder.() -> Unit) {
-        val bytes = HexConverter.parseHexBinary(input.uppercase())
-        CborDecoder(ByteArrayInput(bytes)).block()
-    }
-
-    @Test
-    fun testDecodeIntegers() {
-        withDecoder("0C1903E8") {
-            assertEquals(12L, nextNumber())
-            assertEquals(1000L, nextNumber())
-        }
-        withDecoder("203903e7") {
-            assertEquals(-1L, nextNumber())
-            assertEquals(-1000L, nextNumber())
-        }
-    }
-
-    @Test
-    fun testDecodeStrings() {
-        withDecoder("6568656C6C6F") {
-            assertEquals("hello", nextString())
-        }
-        withDecoder("7828737472696E672074686174206973206C6F6E676572207468616E2032332063686172616374657273") {
-            assertEquals("string that is longer than 23 characters", nextString())
-        }
-    }
-
-    @Test
-    fun testDecodeDoubles() {
-        withDecoder("fb7e37e43c8800759c") {
-            assertEquals(1e+300, nextDouble())
-        }
-        withDecoder("fa47c35000") {
-            assertEquals(100000.0f, nextFloat())
-        }
-    }
-
-    @Test
-    fun testDecodeSimpleObject() {
-        assertEquals(Simple("str"), Cbor.decodeFromHexString(Simple.serializer(), "bf616163737472ff"))
-    }
-
-    @Test
-    fun testDecodeComplicatedObject() {
-        val test = TypesUmbrella(
-            "Hello, world!",
-            42,
-            null,
-            listOf("a", "b"),
-            mapOf(1 to true, 2 to false),
-            Simple("lol"),
-            listOf(Simple("kek")),
-            HexConverter.parseHexBinary("cafe"),
-            HexConverter.parseHexBinary("cafe")
-        )
-        // with maps, lists & strings of indefinite length
-        assertEquals(test, Cbor.decodeFromHexString(
-            TypesUmbrella.serializer(),
-            "bf637374726d48656c6c6f2c20776f726c64216169182a686e756c6c61626c65f6646c6973749f61616162ff636d6170bf01f502f4ff65696e6e6572bf6161636c6f6cff6a696e6e6572734c6973749fbf6161636b656bffff6a62797465537472696e675f42cafeff696279746541727261799f383521ffff"
-        )
-        )
-        // with maps, lists & strings of definite length
-        assertEquals(test, Cbor.decodeFromHexString(
-            TypesUmbrella.serializer(),
-            "a9646c6973748261616162686e756c6c61626c65f6636d6170a202f401f56169182a6a696e6e6572734c69737481a16161636b656b637374726d48656c6c6f2c20776f726c642165696e6e6572a16161636c6f6c6a62797465537472696e6742cafe6962797465417272617982383521"
-        )
-        )
-    }
-
-    /**
-     * Test using example shown on page 11 of [RFC 7049 2.2.2](https://tools.ietf.org/html/rfc7049#section-2.2.2):
-     *
-     * ```
-     * 0b010_11111 0b010_00100 0xaabbccdd 0b010_00011 0xeeff99 0b111_11111
-     *
-     * 5F              -- Start indefinite-length byte string
-     *    44           -- Byte string of length 4
-     *       aabbccdd  -- Bytes content
-     *    43           -- Byte string of length 3
-     *       eeff99    -- Bytes content
-     *    FF           -- "break"
-     *
-     * After decoding, this results in a single byte string with seven
-     * bytes: 0xaabbccddeeff99.
-     * ```
-     */
-    @Test
-    fun testRfc7049IndefiniteByteStringExample() {
-        withDecoder(input = "5F44aabbccdd43eeff99FF") {
-            assertEquals(
-                expected = "aabbccddeeff99",
-                actual = HexConverter.printHexBinary(nextByteString(), lowerCase = true)
-            )
-        }
-    }
-
-    @Test
-    fun testReadByteStringWhenNullable() {
-        /* A1                         # map(1)
-         *    6A                      # text(10)
-         *       62797465537472696E67 # "byteString"
-         *    44                      # bytes(4)
-         *       01020304             # "\x01\x02\x03\x04"
-         */
-        assertEquals(
-            expected = NullableByteString(byteArrayOf(1, 2, 3, 4)),
-            actual = Cbor.decodeFromHexString(
-                deserializer = NullableByteString.serializer(),
-                hex = "a16a62797465537472696e674401020304"
-            )
-        )
-
-        /* A1                         # map(1)
-         *    6A                      # text(10)
-         *       62797465537472696E67 # "byteString"
-         *    F6                      # primitive(22)
-         */
-        assertEquals(
-            expected = NullableByteString(byteString = null),
-            actual = Cbor.decodeFromHexString(
-                deserializer = NullableByteString.serializer(),
-                hex = "a16a62797465537472696e67f6"
-            )
-        )
-    }
-
-    /**
-     * CBOR hex data represents serialized versions of [TypesUmbrella] (which does **not** have a root property 'a') so
-     * decoding to [Simple] (which has the field 'a') is expected to fail.
-     */
-    @Test
-    fun testIgnoreUnknownKeysFailsWhenCborDataIsMissingKeysThatArePresentInKotlinClass() {
-        // with maps & lists of indefinite length
-        assertFailsWithMessage<SerializationException>("Field 'a' is required") {
-            ignoreUnknownKeys.decodeFromHexString(
-                Simple.serializer(),
-                "bf637374726d48656c6c6f2c20776f726c64216169182a686e756c6c61626c65f6646c6973749f61616162ff636d6170bf01f502f4ff65696e6e6572bf6161636c6f6cff6a696e6e6572734c6973749fbf6161636b656bffffff"
-            )
-        }
-
-        // with maps & lists of definite length
-        assertFailsWithMessage<SerializationException>("Field 'a' is required") {
-            ignoreUnknownKeys.decodeFromHexString(
-                Simple.serializer(),
-                "a7646c6973748261616162686e756c6c61626c65f6636d6170a202f401f56169182a6a696e6e6572734c69737481a16161636b656b637374726d48656c6c6f2c20776f726c642165696e6e6572a16161636c6f6c"
-            )
-        }
-    }
-
-    @Test
-    fun testIgnoreUnknownKeysFailsWhenDecodingIncompleteCbor() {
-        /* A3                 # map(3)
-         *    63              # text(3)
-         *       737472       # "str"
-         *    66              # text(6)
-         *       737472696E67 # "string"
-         *    61              # text(1)
-         *       69           # "i"
-         *    00              # unsigned(0)
-         *    66              # text(6)
-         *       69676E6F7265 # "ignore"
-         * (missing value associated with "ignore" key)
-         */
-        assertFailsWithMessage<CborDecodingException>("Unexpected EOF while skipping element") {
-            ignoreUnknownKeys.decodeFromHexString(
-                TypesUmbrella.serializer(),
-                "a36373747266737472696e676169006669676e6f7265"
-            )
-        }
-
-        /* A3                 # map(3)
-         *    63              # text(3)
-         *       737472       # "str"
-         *    66              # text(6)
-         *       737472696E67 # "string"
-         *    61              # text(1)
-         *       69           # "i"
-         *    00              # unsigned(0)
-         *    66              # text(6)
-         *       69676E6F7265 # "ignore"
-         *    A2              # map(2)
-         * (missing map contents associated with "ignore" key)
-         */
-        assertFailsWithMessage<CborDecodingException>("Unexpected EOF while skipping element") {
-            ignoreUnknownKeys.decodeFromHexString(
-                TypesUmbrella.serializer(),
-                "a36373747266737472696e676169006669676e6f7265a2"
-            )
-        }
-    }
-
-    @Test
-    fun testIgnoreUnknownKeysFailsWhenEncounteringPreemptiveBreak() {
-        /* A3                 # map(3)
-         *    63              # text(3)
-         *       737472       # "str"
-         *    66              # text(6)
-         *       737472696E67 # "string"
-         *    66              # text(6)
-         *       69676E6F7265 # "ignore"
-         *    FF              # primitive(*)
-         */
-        assertFailsWithMessage<CborDecodingException>("Expected next data item, but found FF") {
-            ignoreUnknownKeys.decodeFromHexString(
-                TypesUmbrella.serializer(),
-                "a36373747266737472696e676669676e6f7265ff"
-            )
-        }
-    }
-
-    /**
-     * Tests skipping unknown keys associated with values of the following CBOR types:
-     * - Major type 0: an unsigned integer
-     * - Major type 1: a negative integer
-     * - Major type 2: a byte string
-     * - Major type 3: a text string
-     */
-    @Test
-    fun testSkipPrimitives() {
-        /* A4                           # map(4)
-         *    61                        # text(1)
-         *       61                     # "a"
-         *    1B FFFFFFFFFFFFFFFF       # unsigned(18446744073709551615)
-         *    61                        # text(1)
-         *       62                     # "b"
-         *    20                        # negative(0)
-         *    61                        # text(1)
-         *       63                     # "c"
-         *    42                        # bytes(2)
-         *       CAFE                   # "\xCA\xFE"
-         *    61                        # text(1)
-         *       64                     # "d"
-         *    6B                        # text(11)
-         *       48656C6C6F20776F726C64 # "Hello world"
-         */
-        withDecoder("a461611bffffffffffffffff616220616342cafe61646b48656c6c6f20776f726c64") {
-            expectMap(size = 4)
-            expect("a")
-            skipElement() // unsigned(18446744073709551615)
-            expect("b")
-            skipElement() // negative(0)
-            expect("c")
-            skipElement() // "\xCA\xFE"
-            expect("d")
-            skipElement() // "Hello world"
-            expectEof()
-        }
-    }
-
-    /**
-     * Tests skipping unknown keys associated with values (that are empty) of the following CBOR types:
-     * - Major type 2: a byte string
-     * - Major type 3: a text string
-     */
-    @Test
-    fun testSkipEmptyPrimitives() {
-        /* A2       # map(2)
-         *    61    # text(1)
-         *       61 # "a"
-         *    40    # bytes(0)
-         *          # ""
-         *    61    # text(1)
-         *       62 # "b"
-         *    60    # text(0)
-         *          # ""
-         */
-        withDecoder("a2616140616260") {
-            expectMap(size = 2)
-            expect("a")
-            skipElement() // bytes(0)
-            expect("b")
-            skipElement() // text(0)
-            expectEof()
-        }
-    }
-
-    /**
-     * Tests skipping unknown keys associated with values of the following CBOR types:
-     * - Major type 4: an array of data items
-     * - Major type 5: a map of pairs of data items
-     */
-    @Test
-    fun testSkipCollections() {
-        /* A2                                  # map(2)
-         *    61                               # text(1)
-         *       61                            # "a"
-         *    83                               # array(3)
-         *       01                            # unsigned(1)
-         *       18 FF                         # unsigned(255)
-         *       1A 00010000                   # unsigned(65536)
-         *    61                               # text(1)
-         *       62                            # "b"
-         *    A2                               # map(2)
-         *       61                            # text(1)
-         *          78                         # "x"
-         *       67                            # text(7)
-         *          6B6F746C696E78             # "kotlinx"
-         *       61                            # text(1)
-         *          79                         # "y"
-         *       6D                            # text(13)
-         *          73657269616C697A6174696F6E # "serialization"
-         */
-        withDecoder("a26161830118ff1a000100006162a26178676b6f746c696e7861796d73657269616c697a6174696f6e") {
-            expectMap(size = 2)
-            expect("a")
-            skipElement() // [1, 255, 65536]
-            expect("b")
-            skipElement() // {"x": "kotlinx", "y": "serialization"}
-            expectEof()
-        }
-    }
-
-    /**
-     * Tests skipping unknown keys associated with values (empty collections) of the following CBOR types:
-     * - Major type 4: an array of data items
-     * - Major type 5: a map of pairs of data items
-     */
-    @Test
-    fun testSkipEmptyCollections() {
-        /* A2       # map(2)
-         *    61    # text(1)
-         *       61 # "a"
-         *    80    # array(0)
-         *    61    # text(1)
-         *       62 # "b"
-         *    A0    # map(0)
-         */
-        withDecoder("a26161806162a0") {
-            expectMap(size = 2)
-            expect("a")
-            skipElement() // [1, 255, 65536]
-            expect("b")
-            skipElement() // {"x": "kotlinx", "y": "serialization"}
-            expectEof()
-        }
-    }
-
-    /**
-     * Tests skipping unknown keys associated with **indefinite length** values of the following CBOR types:
-     * - Major type 2: a byte string
-     * - Major type 3: a text string
-     * - Major type 4: an array of data items
-     * - Major type 5: a map of pairs of data items
-     */
-    @Test
-    fun testSkipIndefiniteLength() {
-        /* A4                                  # map(4)
-         *    61                               # text(1)
-         *       61                            # "a"
-         *    5F                               # bytes(*)
-         *       42                            # bytes(2)
-         *          CAFE                       # "\xCA\xFE"
-         *       43                            # bytes(3)
-         *          010203                     # "\x01\x02\x03"
-         *       FF                            # primitive(*)
-         *    61                               # text(1)
-         *       62                            # "b"
-         *    7F                               # text(*)
-         *       66                            # text(6)
-         *          48656C6C6F20               # "Hello "
-         *       65                            # text(5)
-         *          776F726C64                 # "world"
-         *       FF                            # primitive(*)
-         *    61                               # text(1)
-         *       63                            # "c"
-         *    9F                               # array(*)
-         *       67                            # text(7)
-         *          6B6F746C696E78             # "kotlinx"
-         *       6D                            # text(13)
-         *          73657269616C697A6174696F6E # "serialization"
-         *       FF                            # primitive(*)
-         *    61                               # text(1)
-         *       64                            # "d"
-         *    BF                               # map(*)
-         *       61                            # text(1)
-         *          31                         # "1"
-         *       01                            # unsigned(1)
-         *       61                            # text(1)
-         *          32                         # "2"
-         *       02                            # unsigned(2)
-         *       61                            # text(1)
-         *          33                         # "3"
-         *       03                            # unsigned(3)
-         *       FF                            # primitive(*)
-         */
-        withDecoder("a461615f42cafe43010203ff61627f6648656c6c6f2065776f726c64ff61639f676b6f746c696e786d73657269616c697a6174696f6eff6164bf613101613202613303ff") {
-            expectMap(size = 4)
-            expect("a")
-            skipElement() // "\xCA\xFE\x01\x02\x03"
-            expect("b")
-            skipElement() // "Hello world"
-            expect("c")
-            skipElement() // ["kotlinx", "serialization"]
-            expect("d")
-            skipElement() // {"1": 1, "2": 2, "3": 3}
-            expectEof()
-        }
-    }
-
-    /**
-     * Tests that skipping unknown keys also skips over associated tags.
-     *
-     * Includes tags on the key, tags on the value, and tags on both key and value.
-     */
-    @Test
-    fun testSkipTags() {
-        /*
-         * A4                                 # map(4)
-         * 61                              # text(1)
-         *    61                           # "a"
-         * CC                              # tag(12)
-         *    1B FFFFFFFFFFFFFFFF          # unsigned(18446744073709551615)
-         * D8 22                           # tag(34)
-         *    61                           # text(1)
-         *       62                        # "b"
-         * 20                              # negative(0)
-         * D8 38                           # tag(56)
-         *    61                           # text(1)
-         *       63                        # "c"
-         * D8 4E                           # tag(78)
-         *    42                           # bytes(2)
-         *       CAFE                      # "\xCA\xFE"
-         * 61                              # text(1)
-         *    64                           # "d"
-         * D8 5A                           # tag(90)
-         *    CC                           # tag(12)
-         *       6B                        # text(11)
-         *          48656C6C6F20776F726C64 # "Hello world"
-         */
-        withDecoder("A46161CC1BFFFFFFFFFFFFFFFFD822616220D8386163D84E42CAFE6164D85ACC6B48656C6C6F20776F726C64") {
-            expectMap(size = 4)
-            expect("a")
-            skipElement() // unsigned(18446744073709551615)
-            expect("b")
-            skipElement() // negative(0)
-            expect("c")
-            skipElement() // "\xCA\xFE"
-            expect("d")
-            skipElement() // "Hello world"
-            expectEof()
-        }
-    }
-
-    @Test
-    fun testDecodeCborWithUnknownField() {
-        assertEquals(
-            expected = Simple("123"),
-            actual = ignoreUnknownKeys.decodeFromHexString(
-                deserializer = Simple.serializer(),
-
-                /* BF           # map(*)
-                 *    61        # text(1)
-                 *       61     # "a"
-                 *    63        # text(3)
-                 *       313233 # "123"
-                 *    61        # text(1)
-                 *       62     # "b"
-                 *    63        # text(3)
-                 *       393837 # "987"
-                 *    FF        # primitive(*)
-                 */
-                hex = "bf616163313233616263393837ff"
-            )
-        )
-    }
-
-    @Test
-    fun testDecodeCborWithUnknownNestedIndefiniteFields() {
-        assertEquals(
-            expected = Simple("123"),
-            actual = ignoreUnknownKeys.decodeFromHexString(
-                deserializer = Simple.serializer(),
-
-                /* BF             # map(*)
-                 *    61          # text(1)
-                 *       61       # "a"
-                 *    63          # text(3)
-                 *       313233   # "123"
-                 *    61          # text(1)
-                 *       62       # "b"
-                 *    BF          # map(*)
-                 *       7F       # text(*)
-                 *          61    # text(1)
-                 *             78 # "x"
-                 *          FF    # primitive(*)
-                 *       A1       # map(1)
-                 *          61    # text(1)
-                 *             79 # "y"
-                 *          0A    # unsigned(10)
-                 *       FF       # primitive(*)
-                 *    61          # text(1)
-                 *       63       # "c"
-                 *    9F          # array(*)
-                 *       01       # unsigned(1)
-                 *       02       # unsigned(2)
-                 *       03       # unsigned(3)
-                 *       FF       # primitive(*)
-                 *    FF          # primitive(*)
-                 */
-                hex = "bf6161633132336162bf7f6178ffa161790aff61639f010203ffff"
-            )
-        )
-    }
-
-    /**
-     * The following CBOR diagnostic output demonstrates the additional fields (prefixed with `+` in front of each line)
-     * present in the encoded CBOR data that does not have associated fields in the Kotlin classes (they will be skipped
-     * over with `ignoreUnknownKeys` is enabled).
-     *
-     * ```diff
-     *   {
-     * +   "extra": [
-     * +     9,
-     * +     8,
-     * +     7
-     * +   ],
-     *     "boxed": [
-     *       [
-     *         "kotlinx.serialization.SimpleSealed.SubSealedA",
-     *         {
-     *           "s": "a",
-     * +         "newA": {
-     * +           "x": 1,
-     * +           "y": 2
-     * +         }
-     *         }
-     *       ],
-     *       [
-     *         "kotlinx.serialization.SimpleSealed.SubSealedB",
-     *         {
-     *           "i": 1
-     *         }
-     *       ]
-     *     ]
-     *   }
-     * ```
-     */
-    @Test
-    fun testDecodeCborWithUnknownKeysInSealedClasses() {
-        /* BF                      # map(*)
-         *    65                   # text(5)
-         *       6578747261        # "extra"
-         *    83                   # array(3)
-         *       09                # unsigned(9)
-         *       08                # unsigned(8)
-         *       07                # unsigned(7)
-         *    65                   # text(5)
-         *       626F786564        # "boxed"
-         *    9F                   # array(*)
-         *       9F                # array(*)
-         *          78 2D          # text(45)
-         *             6B6F746C696E782E73657269616C697A6174696F6E2E53696D706C655365616C65642E5375625365616C656441 # "kotlinx.serialization.SimpleSealed.SubSealedA"
-         *          BF             # map(*)
-         *             61          # text(1)
-         *                73       # "s"
-         *             61          # text(1)
-         *                61       # "a"
-         *             64          # text(4)
-         *                6E657741 # "newA"
-         *             BF          # map(*)
-         *                61       # text(1)
-         *                   78    # "x"
-         *                01       # unsigned(1)
-         *                61       # text(1)
-         *                   79    # "y"
-         *                02       # unsigned(2)
-         *                FF       # primitive(*)
-         *             FF          # primitive(*)
-         *          FF             # primitive(*)
-         *       9F                # array(*)
-         *          78 2D          # text(45)
-         *             6B6F746C696E782E73657269616C697A6174696F6E2E53696D706C655365616C65642E5375625365616C656442 # "kotlinx.serialization.SimpleSealed.SubSealedB"
-         *          BF             # map(*)
-         *             61          # text(1)
-         *                69       # "i"
-         *             01          # unsigned(1)
-         *             FF          # primitive(*)
-         *          FF             # primitive(*)
-         *       FF                # primitive(*)
-         *    FF                   # primitive(*)
-         */
-
-        assertEquals(
-            expected = SealedBox(
-                listOf(
-                    SubSealedA("a"),
-                    SubSealedB(1)
-                )
-            ),
-            actual = ignoreUnknownKeys.decodeFromHexString(
-                SealedBox.serializer(),
-                "bf6565787472618309080765626f7865649f9f782d6b6f746c696e782e73657269616c697a6174696f6e2e53696d706c655365616c65642e5375625365616c656441bf61736161646e657741bf617801617902ffffff9f782d6b6f746c696e782e73657269616c697a6174696f6e2e53696d706c655365616c65642e5375625365616c656442bf616901ffffffff"
-            )
-        )
-    }
-
-    @Test
-    fun testReadCustomByteString() {
-        assertEquals(
-                expected = TypeWithCustomByteString(CustomByteString(0x11, 0x22, 0x33)),
-                actual = Cbor.decodeFromHexString("bf617843112233ff")
-        )
-    }
-
-    @Test
-    fun testReadNullableCustomByteString() {
-        assertEquals(
-                expected = TypeWithNullableCustomByteString(CustomByteString(0x11, 0x22, 0x33)),
-                actual = Cbor.decodeFromHexString("bf617843112233ff")
-        )
-    }
-
-    @Test
-    fun testReadNullCustomByteString() {
-        assertEquals(
-                expected = TypeWithNullableCustomByteString(null),
-                actual = Cbor.decodeFromHexString("bf6178f6ff")
-        )
-    }
-
-    @Test
-    fun testReadValueClassWithByteString() {
-        assertContentEquals(
-            expected = byteArrayOf(0x11, 0x22, 0x33),
-            actual = Cbor.decodeFromHexString<ValueClassWithByteString>("43112233").x
-        )
-    }
-
-    @Test
-    fun testReadValueClassCustomByteString() {
-        assertEquals(
-            expected = ValueClassWithCustomByteString(CustomByteString(0x11, 0x22, 0x33)),
-            actual = Cbor.decodeFromHexString("43112233")
-        )
-    }
-
-    @Test
-    fun testReadValueClassWithUnlabeledByteString() {
-        assertContentEquals(
-            expected = byteArrayOf(
-                0x11,
-                0x22,
-                0x33
-            ),
-            actual = Cbor.decodeFromHexString<ValueClassWithUnlabeledByteString>("43112233").x.x
-        )
-    }
-
-    @Test
-    fun testIgnoresTagsOnStrings() {
-        /*
-         * 84                                # array(4)
-         * 68                             # text(8)
-         *    756E746167676564            # "untagged"
-         * C0                             # tag(0)
-         *    68                          # text(8)
-         *       7461676765642D30         # "tagged-0"
-         * D8 F5                          # tag(245)
-         *    6A                          # text(10)
-         *       7461676765642D323435     # "tagged-244"
-         * D9 3039                        # tag(12345)
-         *    6C                          # text(12)
-         *       7461676765642D3132333435 # "tagged-12345"
-         *
-         */
-        withDecoder("8468756E746167676564C0687461676765642D30D8F56A7461676765642D323435D930396C7461676765642D3132333435") {
-            assertEquals(4, startArray())
-            assertEquals("untagged", nextString())
-            assertEquals("tagged-0", nextString())
-            assertEquals("tagged-245", nextString())
-            assertEquals("tagged-12345", nextString())
-        }
-    }
-
-    @Test
-    fun testIgnoresTagsOnNumbers() {
-        /*
-         * 86                     # array(6)
-         * 18 7B                  # unsigned(123)
-         * C0                     # tag(0)
-         *    1A 0001E240         # unsigned(123456)
-         * D8 F5                  # tag(245)
-         *    1A 000F423F         # unsigned(999999)
-         * D9 3039                # tag(12345)
-         *    38 31               # negative(49)
-         * D8 22                  # tag(34)
-         *    FB 3FE161F9F01B866E # primitive(4603068020252444270)
-         * D9 0237                # tag(567)
-         *    FB 401999999999999A # primitive(4618891777831180698)
-         */
-        withDecoder("86187BC01A0001E240D8F51A000F423FD930393831D822FB3FE161F9F01B866ED90237FB401999999999999A") {
-            assertEquals(6, startArray())
-            assertEquals(123, nextNumber())
-            assertEquals(123456, nextNumber())
-            assertEquals(999999, nextNumber())
-            assertEquals(-50, nextNumber())
-            assertEquals(0.54321, nextDouble(), 0.00001)
-            assertEquals(6.4, nextDouble(), 0.00001)
-        }
-    }
-
-    @Test
-    fun testIgnoresTagsOnArraysAndMaps() {
-        /*
-         * A2                                  # map(2)
-         * 63                                  # text(3)
-         *    6D6170                           # "map"
-         * D8 7B                               # tag(123)
-         *    A1                               # map(1)
-         *       68                            # text(8)
-         *          74686973206D6170           # "this map"
-         *       6D                            # text(13)
-         *          69732074616767656420313233 # "is tagged 123"
-         * 65                                  # text(5)
-         *    6172726179                       # "array"
-         * DA 0012D687                         # tag(1234567)
-         *    83                               # array(3)
-         *       6A                            # text(10)
-         *          74686973206172726179       # "this array"
-         *       69                            # text(9)
-         *          697320746167676564         # "is tagged"
-         *       67                            # text(7)
-         *          31323334353637             # "1234567"
-         */
-        withDecoder("A2636D6170D87BA16874686973206D61706D69732074616767656420313233656172726179DA0012D687836A74686973206172726179696973207461676765646731323334353637") {
-            assertEquals(2, startMap())
-            assertEquals("map", nextString())
-            assertEquals(1, startMap())
-            assertEquals("this map", nextString())
-            assertEquals("is tagged 123", nextString())
-            assertEquals("array", nextString())
-            assertEquals(3, startArray())
-            assertEquals("this array", nextString())
-            assertEquals("is tagged", nextString())
-            assertEquals("1234567", nextString())
-        }
-    }
-}
-
-private fun CborDecoder.expect(expected: String) {
-    assertEquals(expected, actual = nextString(), "string")
-}
-
-private fun CborDecoder.expectMap(size: Int) {
-    assertEquals(size, actual = startMap(), "map size")
-}
-
-private fun CborDecoder.expectEof() {
-    assertTrue(isEof(), "Expected EOF.")
-}
diff --git a/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborRootLevelNullsTest.kt b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborRootLevelNullsTest.kt
index 3e54834..0106ccc 100644
--- a/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborRootLevelNullsTest.kt
+++ b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborRootLevelNullsTest.kt
@@ -15,7 +15,9 @@
     @Test
     fun testNull() {
         val obj: Simple? = null
-        val content = Cbor.encodeToByteArray(Simple.serializer().nullable, obj)
-        assertTrue(content.contentEquals(byteArrayOf(0xf6.toByte())))
+        listOf(Cbor, Cbor { useDefiniteLengthEncoding = true }).forEach {
+            val content = it.encodeToByteArray(Simple.serializer().nullable, obj)
+            assertTrue(content.contentEquals(byteArrayOf(0xf6.toByte())))
+        }
     }
 }
diff --git a/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborTaggedTest.kt b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborTaggedTest.kt
new file mode 100644
index 0000000..fa884c0
--- /dev/null
+++ b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborTaggedTest.kt
@@ -0,0 +1,808 @@
+/*
+ * Copyright 2017-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+@file:OptIn(ExperimentalUnsignedTypes::class)
+
+package kotlinx.serialization.cbor
+
+import kotlinx.serialization.*
+import kotlinx.serialization.cbor.internal.*
+import kotlin.test.*
+
+
+@Serializable
+data class DataWithTags(
+    @ValueTags(12uL)
+    val a: ULong,
+
+    @KeyTags(34uL)
+    val b: Int,
+
+    @KeyTags(56uL)
+    @ValueTags(78uL)
+    @ByteString val c: ByteArray,
+
+    @ValueTags(90uL, 12uL)
+    val d: String
+) {
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null || this::class != other::class) return false
+
+        other as DataWithTags
+
+        if (a != other.a) return false
+        if (b != other.b) return false
+        if (!c.contentEquals(other.c)) return false
+        return d == other.d
+    }
+
+    override fun hashCode(): Int {
+        var result = a.hashCode()
+        result = 31 * result + b
+        result = 31 * result + c.contentHashCode()
+        result = 31 * result + d.hashCode()
+        return result
+    }
+}
+
+class CborTaggedTest {
+
+    private val reference = DataWithTags(
+        a = 0xFFFFFFFuL,
+        b = -1,
+        c = byteArrayOf(0xCA.toByte(), 0xFE.toByte()),
+        d = "Hello World"
+    )
+
+    /*
+     * BF                                 # map(*)
+     *    61                              # text(1)
+     *       61                           # "a"
+     *    CC                              # tag(12)
+     *       1A 0FFFFFFF                  # unsigned(268435455)
+     *    D8 22                           # tag(34)
+     *       61                           # text(1)
+     *          62                        # "b"
+     *    20                              # negative(0)
+     *    D8 38                           # tag(56)
+     *       61                           # text(1)
+     *          63                        # "c"
+     *    D8 4E                           # tag(78)
+     *       42                           # bytes(2)
+     *          CAFE                      # "\xCA\xFE"
+     *    61                              # text(1)
+     *       64                           # "d"
+     *    D8 5A                           # tag(90)
+     *       CC                           # tag(12)
+     *          6B                        # text(11)
+     *             48656C6C6F20576F726C64 # "Hello World"
+     *    FF                              # primitive(*)
+     */
+    private val referenceHexString =
+        "bf6161cc1a0fffffffd822616220d8386163d84e42cafe6164d85acc6b48656c6c6f20576f726c64ff"
+
+    /*
+     * A4                                 # map(4)
+     *    61                              # text(1)
+     *       61                           # "a"
+     *    CC                              # tag(12)
+     *       1A 0FFFFFFF                  # unsigned(268435455)
+     *    D8 22                           # tag(34)
+     *       61                           # text(1)
+     *          62                        # "b"
+     *    20                              # negative(0)
+     *    D8 38                           # tag(56)
+     *       61                           # text(1)
+     *          63                        # "c"
+     *    D8 4E                           # tag(78)
+     *       42                           # bytes(2)
+     *          CAFE                      # "\xCA\xFE"
+     *    61                              # text(1)
+     *       64                           # "d"
+     *    D8 5A                           # tag(90)
+     *       CC                           # tag(12)
+     *          6B                        # text(11)
+     *             48656C6C6F20576F726C64 # "Hello World"
+     */
+    private val referenceHexStringDefLen =
+        "a46161cc1a0fffffffd822616220d8386163d84e42cafe6164d85acc6b48656c6c6f20576f726c64"
+
+    /*
+     * BF                                 # map(*)
+     *    61                              # text(1)
+     *       61                           # "a"
+     *    CC                              # tag(12)
+     *       1A 0FFFFFFF                  # unsigned(268435455)
+     *    61                              # text(1)
+     *       62                           # "b"
+     *    20                              # negative(0)
+     *    61                              # text(1)
+     *       63                           # "c"
+     *    D8 4E                           # tag(78)
+     *       42                           # bytes(2)
+     *          CAFE                      # "\xCA\xFE"
+     *    61                              # text(1)
+     *       64                           # "d"
+     *    D8 5A                           # tag(90)
+     *       CC                           # tag(12)
+     *          6B                        # text(11)
+     *             48656C6C6F20576F726C64 # "Hello World"
+     *    FF                              # primitive(*)
+     */
+    private val noKeyTags = "bf6161cc1a0fffffff6162206163d84e42cafe6164d85acc6b48656c6c6f20576f726c64ff"
+
+    /*
+     * A4                                 # map(4)
+     *    61                              # text(1)
+     *       61                           # "a"
+     *    CC                              # tag(12)
+     *       1A 0FFFFFFF                  # unsigned(268435455)
+     *    61                              # text(1)
+     *       62                           # "b"
+     *    20                              # negative(0)
+     *    61                              # text(1)
+     *       63                           # "c"
+     *    D8 4E                           # tag(78)
+     *       42                           # bytes(2)
+     *          CAFE                      # "\xCA\xFE"
+     *    61                              # text(1)
+     *       64                           # "d"
+     *    D8 5A                           # tag(90)
+     *       CC                           # tag(12)
+     *          6B                        # text(11)
+     *             48656C6C6F20576F726C64 # "Hello World"
+     *
+     *
+     */
+    private val noKeyTagsDefLen = "a46161cc1a0fffffff6162206163d84e42cafe6164d85acc6b48656c6c6f20576f726c64"
+
+    /*
+     * BF                           # map(*)
+     *    61                        # text(1)
+     *       61                     # "a"
+     *    1A 0FFFFFFF               # unsigned(268435455)
+     *    D8 22                     # tag(34)
+     *       61                     # text(1)
+     *          62                  # "b"
+     *    20                        # negative(0)
+     *    D8 38                     # tag(56)
+     *       61                     # text(1)
+     *          63                  # "c"
+     *    42                        # bytes(2)
+     *       CAFE                   # "\xCA\xFE"
+     *    61                        # text(1)
+     *       64                     # "d"
+     *    6B                        # text(11)
+     *       48656C6C6F20576F726C64 # "Hello World"
+     *    FF                        # primitive(*)
+     */
+    private val noValueTags = "bf61611a0fffffffd822616220d838616342cafe61646b48656c6c6f20576f726c64ff"
+
+    /*
+     * BF                           # map(*)
+     *    61                        # text(1)
+     *       61                     # "a"
+     *    1A 0FFFFFFF               # unsigned(268435455)
+     *    61                        # text(1)
+     *       62                     # "b"
+     *    20                        # negative(0)
+     *    61                        # text(1)
+     *       63                     # "c"
+     *    42                        # bytes(2)
+     *       CAFE                   # "\xCA\xFE"
+     *    61                        # text(1)
+     *       64                     # "d"
+     *    6B                        # text(11)
+     *       48656C6C6F20576F726C64 # "Hello World"
+     *    FF                        # primitive(*)
+     *
+     */
+    private val noTags = "bf61611a0fffffff616220616342cafe61646b48656c6c6f20576f726c64ff"
+
+    /*
+     * A4                           # map(4)
+     *    61                        # text(1)
+     *       61                     # "a"
+     *    1A 0FFFFFFF               # unsigned(268435455)
+     *    61                        # text(1)
+     *       62                     # "b"
+     *    20                        # negative(0)
+     *    61                        # text(1)
+     *       63                     # "c"
+     *    42                        # bytes(2)
+     *       CAFE                   # "\xCA\xFE"
+     *    61                        # text(1)
+     *       64                     # "d"
+     *    6B                        # text(11)
+     *       48656C6C6F20576F726C64 # "Hello World"
+     *
+     */
+    private val noTagsDefLen = "a461611a0fffffff616220616342cafe61646b48656c6c6f20576f726c64"
+
+    @Test
+    fun writeReadVerifyTaggedClass() {
+        assertEquals(referenceHexString, Cbor {
+            useDefiniteLengthEncoding = false
+            encodeKeyTags = true
+            encodeValueTags = true
+            encodeObjectTags = true
+            verifyKeyTags = true
+            verifyValueTags = true
+            verifyObjectTags = true
+        }.encodeToHexString(DataWithTags.serializer(), reference))
+        assertEquals(
+            referenceHexStringDefLen,
+            Cbor {
+                useDefiniteLengthEncoding = true
+                encodeKeyTags = true
+                encodeValueTags = true
+                encodeObjectTags = true
+                verifyKeyTags = true
+                verifyValueTags = true
+                verifyObjectTags = true
+            }.encodeToHexString(DataWithTags.serializer(), reference)
+        )
+        assertEquals(reference, Cbor.CoseCompliant.decodeFromHexString(DataWithTags.serializer(), referenceHexString))
+        assertEquals(
+            reference,
+            Cbor.CoseCompliant.decodeFromHexString(DataWithTags.serializer(), referenceHexStringDefLen)
+        )
+    }
+
+    @Test
+    fun writeReadUntaggedKeys() {
+        assertEquals(noKeyTags, Cbor {
+            encodeKeyTags = false
+            encodeValueTags = true
+            encodeObjectTags = true
+            verifyKeyTags = false
+            verifyValueTags = true
+            verifyObjectTags = true
+        }.encodeToHexString(DataWithTags.serializer(), reference))
+        assertEquals(
+            noKeyTagsDefLen,
+            Cbor {
+                useDefiniteLengthEncoding = true
+                encodeKeyTags = false
+                encodeValueTags = true
+                encodeObjectTags = true
+                verifyKeyTags = true
+                verifyValueTags = true
+                verifyObjectTags = true
+            }.encodeToHexString(
+                DataWithTags.serializer(),
+                reference
+            )
+        )
+        assertEquals(reference, Cbor {
+            encodeKeyTags = true
+            encodeValueTags = true
+            encodeObjectTags = true
+            verifyValueTags = true
+            verifyObjectTags = true
+            verifyKeyTags = false
+        }.decodeFromHexString(noKeyTags))
+        assertEquals(reference, Cbor {
+            encodeKeyTags = true
+            encodeValueTags = true
+            encodeObjectTags = true
+            verifyValueTags = true
+            verifyObjectTags = true
+            verifyKeyTags = false
+        }.decodeFromHexString(noKeyTagsDefLen))
+        assertEquals(reference, Cbor {
+            encodeKeyTags = true
+            encodeValueTags = true
+            encodeObjectTags = true
+            verifyValueTags = true
+            verifyObjectTags = true
+            verifyKeyTags = false
+        }.decodeFromHexString(referenceHexString))
+
+        assertFailsWith(CborDecodingException::class) {
+            Cbor.CoseCompliant.decodeFromHexString(
+                DataWithTags.serializer(),
+                noKeyTags
+            )
+        }
+
+        assertFailsWith(CborDecodingException::class) {
+            Cbor {
+                encodeKeyTags = true
+                encodeValueTags = true
+                encodeObjectTags = true
+                verifyValueTags = true
+                verifyObjectTags = true
+                verifyKeyTags = false
+            }.decodeFromHexString(DataWithTags.serializer(), noValueTags)
+        }
+    }
+
+    @Test
+    fun writeReadUntaggedValues() {
+        assertEquals(
+            noValueTags,
+            Cbor {
+                encodeKeyTags = true
+                encodeObjectTags = true
+                verifyKeyTags = true
+                verifyValueTags = true
+                verifyObjectTags = true
+                encodeValueTags = false
+            }.encodeToHexString(DataWithTags.serializer(), reference)
+        )
+        assertEquals(reference, Cbor {
+            encodeKeyTags = true
+            encodeValueTags = true
+            encodeObjectTags = true
+            verifyKeyTags = true
+            verifyObjectTags = true
+            verifyValueTags = false
+        }.decodeFromHexString(noValueTags))
+        assertEquals(reference, Cbor {
+            encodeKeyTags = true
+            encodeValueTags = true
+            encodeObjectTags = true
+            verifyKeyTags = true
+            verifyObjectTags = true
+            verifyValueTags = false
+        }.decodeFromHexString(referenceHexString))
+
+        assertFailsWith(CborDecodingException::class) {
+            Cbor {
+                encodeKeyTags = true
+                encodeValueTags = true
+                encodeObjectTags = true
+                verifyKeyTags = true
+                verifyValueTags = true
+                verifyObjectTags = true
+            }.decodeFromHexString(
+                DataWithTags.serializer(),
+                noValueTags
+            )
+        }
+
+        assertFailsWith(CborDecodingException::class) {
+            Cbor {
+                encodeKeyTags = true
+                encodeValueTags = true
+                encodeObjectTags = true
+                verifyKeyTags = true
+                verifyObjectTags = true
+                verifyValueTags = false
+            }.decodeFromHexString(
+                DataWithTags.serializer(),
+                noKeyTags
+            )
+        }
+
+    }
+
+    @Test
+    fun writeReadUntaggedEverything() {
+        assertEquals(
+            noTags,
+            Cbor {
+                encodeObjectTags = true
+                verifyKeyTags = true
+                verifyValueTags = true
+                verifyObjectTags = true
+                encodeValueTags = false
+                encodeKeyTags = false
+            }.encodeToHexString(DataWithTags.serializer(), reference)
+        )
+        assertEquals(
+            noTagsDefLen,
+            Cbor {
+                encodeObjectTags = true
+                verifyKeyTags = true
+                verifyValueTags = true
+                verifyObjectTags = true
+                encodeValueTags = false
+                encodeKeyTags = false
+                useDefiniteLengthEncoding = true
+            }.encodeToHexString(DataWithTags.serializer(), reference)
+        )
+
+        assertEquals(reference, Cbor {
+            encodeKeyTags = true
+            encodeValueTags = true
+            encodeObjectTags = true
+            verifyObjectTags = true
+            verifyKeyTags = false
+            verifyValueTags = false
+        }.decodeFromHexString(noTags))
+
+        assertEquals(reference, Cbor {
+            encodeKeyTags = true
+            encodeValueTags = true
+            encodeObjectTags = true
+            verifyObjectTags = true
+            verifyKeyTags = false
+            verifyValueTags = false
+        }.decodeFromHexString(noTagsDefLen))
+
+        assertEquals(reference, Cbor {
+            encodeKeyTags = true
+            encodeValueTags = true
+            encodeObjectTags = true
+            verifyObjectTags = true
+            verifyKeyTags = false
+            verifyValueTags = false
+            useDefiniteLengthEncoding = true
+        }.decodeFromHexString(noTags))
+
+        assertEquals(reference, Cbor {
+            encodeKeyTags = true
+            encodeValueTags = true
+            encodeObjectTags = true
+            verifyObjectTags = true
+            verifyKeyTags = false
+            verifyValueTags = false
+            useDefiniteLengthEncoding = true
+        }.decodeFromHexString(noTagsDefLen))
+
+        assertEquals(reference, Cbor {
+            encodeKeyTags = true
+            encodeValueTags = true
+            encodeObjectTags = true
+            verifyObjectTags = true
+            verifyKeyTags = false
+            verifyValueTags = false
+        }.decodeFromHexString(noKeyTags))
+
+        assertEquals(reference, Cbor {
+            encodeKeyTags = true
+            encodeValueTags = true
+            encodeObjectTags = true
+            verifyObjectTags = true
+            verifyKeyTags = false
+            verifyValueTags = false
+        }.decodeFromHexString(noValueTags))
+
+        assertEquals(reference, Cbor {
+            encodeKeyTags = true
+            encodeValueTags = true
+            encodeObjectTags = true
+            verifyObjectTags = true
+            verifyKeyTags = false
+            verifyValueTags = false
+        }.decodeFromHexString(referenceHexString))
+
+        assertFailsWith(CborDecodingException::class) {
+            Cbor {
+                encodeKeyTags = true
+                encodeValueTags = true
+                encodeObjectTags = true
+                verifyKeyTags = true
+                verifyValueTags = true
+                verifyObjectTags = true
+            }.decodeFromHexString(
+                DataWithTags.serializer(),
+                noTags
+            )
+        }
+
+    }
+
+    @Test
+    fun wrongTags() {
+        val wrongTag55ForPropertyC = "A46161CC1A0FFFFFFFD822616220D8376163D84E42CAFE6164D85ACC6B48656C6C6F20576F726C64"
+        listOf(
+            Cbor {
+                encodeKeyTags = true
+                encodeValueTags = true
+                encodeObjectTags = true
+                verifyKeyTags = true
+                verifyValueTags = true
+                verifyObjectTags = true
+            },
+            Cbor {
+                encodeKeyTags = true
+                encodeValueTags = true
+                encodeObjectTags = true
+                verifyKeyTags = true
+                verifyValueTags = true
+                verifyObjectTags = true
+                useDefiniteLengthEncoding = true
+            }).forEach { cbor ->
+
+            assertContains(
+                assertFailsWith(
+                    CborDecodingException::class,
+                    message = "CBOR tags [55] do not match declared tags [56]"
+                ) {
+                    cbor.decodeFromHexString(
+                        DataWithTags.serializer(),
+                        wrongTag55ForPropertyC
+                    )
+                }.message ?: "", "CBOR tags [55] do not match expected tags [56]"
+            )
+        }
+        listOf(
+            Cbor {
+                encodeKeyTags = true
+                encodeValueTags = true
+                encodeObjectTags = true
+                verifyValueTags = true
+                verifyObjectTags = true
+                verifyKeyTags = false
+                useDefiniteLengthEncoding = true
+            },
+            Cbor {
+                encodeKeyTags = true
+                encodeValueTags = true
+                encodeObjectTags = true
+                verifyValueTags = true
+                verifyObjectTags = true
+                verifyKeyTags = false
+            }).forEach { cbor ->
+            assertEquals(reference, cbor.decodeFromHexString(wrongTag55ForPropertyC))
+        }
+    }
+
+
+    @Test
+    fun objectTags() {
+        /**
+         * D9 0539         # tag(1337)
+         *    BF           # map(*)
+         *       63        # text(3)
+         *          616C67 # "alg"
+         *       13        # unsigned(19)
+         *       FF        # primitive(*)
+         */
+        val referenceHexString = "d90539bf63616c6713ff"
+        val untaggedHexString = "bf63616c6713ff" //no ObjectTags
+        val reference = ClassAsTagged(19)
+
+        val cbor = Cbor {
+            encodeKeyTags = true
+            encodeValueTags = true
+            verifyKeyTags = true
+            verifyValueTags = true
+            useDefiniteLengthEncoding = false
+            verifyObjectTags = true
+            encodeObjectTags = true
+        }
+
+        assertEquals(referenceHexString, cbor.encodeToHexString(ClassAsTagged.serializer(), reference))
+        assertEquals(reference, cbor.decodeFromHexString(ClassAsTagged.serializer(), referenceHexString))
+
+        assertEquals(
+            reference,
+            Cbor { verifyObjectTags = false }.decodeFromHexString(ClassAsTagged.serializer(), referenceHexString)
+        )
+
+        assertEquals(
+            untaggedHexString,
+            Cbor { encodeObjectTags = false }.encodeToHexString(ClassAsTagged.serializer(), reference)
+        )
+
+
+        assertEquals(
+            reference,
+            Cbor { verifyObjectTags = false }.decodeFromHexString(ClassAsTagged.serializer(), untaggedHexString)
+        )
+
+        assertContains(
+            assertFailsWith(CborDecodingException::class) {
+                cbor.decodeFromHexString(ClassAsTagged.serializer(), untaggedHexString)
+            }.message ?: "", "do not match expected tags"
+        )
+
+        /**
+         * 81                 # array(1)
+         *    D9 0539         # tag(1337)
+         *       A1           # map(1)
+         *          63        # text(3)
+         *             616C67 # "alg"
+         *          13        # unsigned(19)
+         */
+        val listOfObjectTagged = listOf(reference)
+        assertEquals("81d90539a163616c6713", Cbor.CoseCompliant.encodeToHexString(listOfObjectTagged))
+
+
+    }
+
+
+    @Test
+    fun nestedObjectTags() {
+        /**
+         * BF                                 # map(*)
+         *    63                              # text(3)
+         *       616C67                       # "alg"
+         *    0D                              # unsigned(13)
+         *    64                              # text(4)
+         *       696E7473                     # "ints"
+         *    D3                              # tag(19)
+         *       9F                           # array(*)
+         *          18 1A                     # unsigned(26)
+         *          18 18                     # unsigned(24)
+         *          FF                        # primitive(*)
+         *    69                              # text(9)
+         *       6F626A546167676564           # "objTagged"
+         *    D8 2A                           # tag(42)
+         *       D9 0539                      # tag(1337)
+         *          BF                        # map(*)
+         *             63                     # text(3)
+         *                616C67              # "alg"
+         *             13                     # unsigned(19)
+         *             FF                     # primitive(*)
+         *    6E                              # text(14)
+         *       6F626A5461676765644172726179 # "objTaggedArray"
+         *    D8 2A                           # tag(42)
+         *       9F                           # array(*)
+         *          D9 0539                   # tag(1337)
+         *             BF                     # map(*)
+         *                63                  # text(3)
+         *                   616C67           # "alg"
+         *                19 03E8             # unsigned(1000)
+         *                FF                  # primitive(*)
+         *          FF                        # primitive(*)
+         *    FF                              # primitive(*)
+         */
+        val referenceHexString =
+            "bf63616c670d64696e7473d39f181a1818ff696f626a546167676564d82ad90539bf63616c6713ff6e6f626a5461676765644172726179d9038f9fd90539bf63616c671903e8ffffff"
+        val referenceHexStringWithBogusTag =
+            "bf63616c670d64696e7473d3d49f181a1818ff696f626a546167676564d82ad90539bf63616c6713ff6e6f626a5461676765644172726179d9038f9fd90539bf63616c671903e8ffffff"
+        val referenceHexStringWithMissingTag =
+            "bf63616c670d64696e74739f181a1818ff696f626a546167676564d82ad90539bf63616c6713ff6e6f626a5461676765644172726179d9038f9fd90539bf63616c671903e8ffffff"
+
+        val superfluousTagged =
+            "bf63616c670d64696e7473d39f181a1818ff696f626a546167676564d82ad90540d90539bf63616c6713ff6e6f626a5461676765644172726179d9038f9fd90539bf63616c671903e8ffffff"
+        val superfluousWrongTaggedTagged =
+            "bf63616c670d64696e7473d39f181a1818ff696f626a546167676564d82bd82ad90540d90539bf63616c6713ff6e6f626a5461676765644172726179d9038f9fd90539bf63616c671903e8ffffff"
+        val untaggedHexString =
+            "bf63616c670d64696e7473d39f181a1818ff696f626a546167676564d82abf63616c6713ff6e6f626a5461676765644172726179d9038f9fbf63616c671903e8ffffff" //no ObjectTags
+        val reference = NestedTagged(
+            alg = 13,
+            ints = intArrayOf(26, 24),
+            objTagged = ClassAsTagged(19),
+            objTaggedArray = listOf((ClassAsTagged(1000)))
+        )
+        val cbor = Cbor {
+            encodeKeyTags = true
+            verifyKeyTags = true
+            verifyValueTags = true
+            useDefiniteLengthEncoding = false
+            verifyObjectTags = true
+            encodeObjectTags = true
+            encodeValueTags = true
+        }
+        assertEquals(referenceHexString, cbor.encodeToHexString(NestedTagged.serializer(), reference))
+        assertEquals(reference, cbor.decodeFromHexString(NestedTagged.serializer(), referenceHexString))
+
+        assertEquals(
+            "More tags found than the 1 tags specified",
+            assertFailsWith(CborDecodingException::class, message = "More tags found than the 1 tags specified") {
+                cbor.decodeFromHexString(NestedTagged.serializer(), referenceHexStringWithBogusTag)
+            }.message
+        )
+
+        assertEquals(
+            "CBOR tags null do not match expected tags [19]",
+            assertFailsWith(CborDecodingException::class, message = "CBOR tags null do not match expected tags [19]") {
+                cbor.decodeFromHexString(NestedTagged.serializer(), referenceHexStringWithMissingTag)
+            }.message
+        )
+
+
+        assertEquals(
+            reference,
+            Cbor {
+                encodeKeyTags = true
+                encodeValueTags = true
+                encodeObjectTags = true
+                verifyKeyTags = true
+                verifyValueTags = true
+                verifyObjectTags = false
+            }.decodeFromHexString(NestedTagged.serializer(), referenceHexString)
+        )
+
+        assertEquals(
+            reference,
+            Cbor {
+                encodeKeyTags = true
+                encodeValueTags = true
+                encodeObjectTags = true
+                verifyKeyTags = true
+                verifyValueTags = true
+                verifyObjectTags = false
+            }.decodeFromHexString(NestedTagged.serializer(), superfluousTagged)
+        )
+
+        assertEquals(
+            untaggedHexString,
+            Cbor {
+                encodeKeyTags = true
+                encodeValueTags = true
+                verifyKeyTags = true
+                verifyValueTags = true
+                verifyObjectTags = true
+                encodeObjectTags = false
+            }.encodeToHexString(NestedTagged.serializer(), reference)
+        )
+
+
+        assertEquals(
+            reference,
+            Cbor {
+                encodeKeyTags = true
+                encodeValueTags = true
+                encodeObjectTags = true
+                verifyKeyTags = true
+                verifyValueTags = true
+                verifyObjectTags = false
+            }.decodeFromHexString(NestedTagged.serializer(), untaggedHexString)
+        )
+
+        assertContains(
+            assertFailsWith(CborDecodingException::class) {
+                cbor.decodeFromHexString(NestedTagged.serializer(), untaggedHexString)
+            }.message ?: "", "do not match expected tags"
+        )
+
+        assertContains(
+            assertFailsWith(CborDecodingException::class) {
+                Cbor {
+                    encodeKeyTags = true
+                    encodeValueTags = true
+                    encodeObjectTags = true
+                    verifyKeyTags = true
+                    verifyValueTags = true
+                    verifyObjectTags = false
+                }.decodeFromHexString(
+                    NestedTagged.serializer(),
+                    superfluousWrongTaggedTagged
+                )
+            }.message ?: "", "do not start with specified tags"
+        )
+
+
+    }
+
+    @ObjectTags(1337uL)
+    @Serializable
+    data class ClassAsTagged(
+        @SerialName("alg")
+        val alg: Int,
+    )
+
+    @Serializable
+    data class NestedTagged(
+        @SerialName("alg")
+        val alg: Int,
+        @ValueTags(19u)
+        val ints: IntArray,
+
+        @ValueTags(42u)
+        val objTagged: ClassAsTagged,
+        @ValueTags(911u)
+        val objTaggedArray: List<ClassAsTagged>
+    ) {
+        override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (other !is NestedTagged) return false
+
+            if (alg != other.alg) return false
+            if (!ints.contentEquals(other.ints)) return false
+            if (objTagged != other.objTagged) return false
+            if (objTaggedArray != other.objTaggedArray) return false
+
+            return true
+        }
+
+        override fun hashCode(): Int {
+            var result = alg
+            result = 31 * result + ints.contentHashCode()
+            result = 31 * result + objTagged.hashCode()
+            result = 31 * result + objTaggedArray.hashCode()
+            return result
+        }
+    }
+
+
+}
\ No newline at end of file
diff --git a/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborWriterTest.kt b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborWriterTest.kt
index da7b128..330d4ff 100644
--- a/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborWriterTest.kt
+++ b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborWriterTest.kt
@@ -34,6 +34,25 @@
     }
 
     @Test
+    fun writeComplicatedClassDefLen() {
+        val test = TypesUmbrella(
+            "Hello, world!",
+            42,
+            null,
+            listOf("a", "b"),
+            mapOf(1 to true, 2 to false),
+            Simple("lol"),
+            listOf(Simple("kek")),
+            HexConverter.parseHexBinary("cafe"),
+            HexConverter.parseHexBinary("cafe")
+        )
+        assertEquals(
+            "a9637374726d48656c6c6f2c20776f726c64216169182a686e756c6c61626c65f6646c6973748261616162636d6170a201f502f465696e6e6572a16161636c6f6c6a696e6e6572734c69737481a16161636b656b6a62797465537472696e6742cafe6962797465417272617982383521",
+            Cbor { useDefiniteLengthEncoding = true }.encodeToHexString(TypesUmbrella.serializer(), test)
+        )
+    }
+
+    @Test
     fun writeManyNumbers() {
         val test = NumberTypesUmbrella(
             100500,
@@ -100,26 +119,53 @@
     }
 
     @Test
+    fun testOmitNullForNullableByteString() {
+        /* BF                         # map(*)
+         *    FF                      # primitive(*)
+         */
+        assertEquals(
+            expected = "bfff",
+            actual = Cbor.encodeToHexString(
+                serializer = NullableByteStringDefaultNull.serializer(),
+                value = NullableByteStringDefaultNull(byteString = null)
+            )
+        )
+    }
+
+    @Test
+    fun testOmitNullDefLenForNullableByteString() {
+        /* A0                         # map(0)
+         */
+        assertEquals(
+            expected = "a0",
+            actual = Cbor { useDefiniteLengthEncoding = true }.encodeToHexString(
+                serializer = NullableByteStringDefaultNull.serializer(),
+                value = NullableByteStringDefaultNull(byteString = null)
+            )
+        )
+    }
+
+    @Test
     fun testWriteCustomByteString() {
         assertEquals(
-                expected = "bf617843112233ff",
-                actual = Cbor.encodeToHexString(TypeWithCustomByteString(CustomByteString(0x11, 0x22, 0x33)))
+            expected = "bf617843112233ff",
+            actual = Cbor.encodeToHexString(TypeWithCustomByteString(CustomByteString(0x11, 0x22, 0x33)))
         )
     }
 
     @Test
     fun testWriteNullableCustomByteString() {
         assertEquals(
-                expected = "bf617843112233ff",
-                actual = Cbor.encodeToHexString(TypeWithNullableCustomByteString(CustomByteString(0x11, 0x22, 0x33)))
+            expected = "bf617843112233ff",
+            actual = Cbor.encodeToHexString(TypeWithNullableCustomByteString(CustomByteString(0x11, 0x22, 0x33)))
         )
     }
 
     @Test
     fun testWriteNullCustomByteString() {
         assertEquals(
-                expected = "bf6178f6ff",
-                actual = Cbor.encodeToHexString(TypeWithNullableCustomByteString(null))
+            expected = "bf6178f6ff",
+            actual = Cbor.encodeToHexString(TypeWithNullableCustomByteString(null))
         )
     }
 
diff --git a/formats/cbor/commonTest/src/kotlinx/serialization/cbor/SampleClasses.kt b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/SampleClasses.kt
index e4418f4..8feb491 100644
--- a/formats/cbor/commonTest/src/kotlinx/serialization/cbor/SampleClasses.kt
+++ b/formats/cbor/commonTest/src/kotlinx/serialization/cbor/SampleClasses.kt
@@ -15,15 +15,15 @@
 
 @Serializable
 data class TypesUmbrella(
-        val str: String,
-        val i: Int,
-        val nullable: Double?,
-        val list: List<String>,
-        val map: Map<Int, Boolean>,
-        val inner: Simple,
-        val innersList: List<Simple>,
-        @ByteString val byteString: ByteArray,
-        val byteArray: ByteArray
+    val str: String,
+    val i: Int,
+    val nullable: Double?,
+    val list: List<String>,
+    val map: Map<Int, Boolean>,
+    val inner: Simple,
+    val innersList: List<Simple>,
+    @ByteString val byteString: ByteArray,
+    val byteArray: ByteArray
 ) {
     override fun equals(other: Any?): Boolean {
         if (this === other) return true
@@ -60,12 +60,12 @@
 
 @Serializable
 data class NumberTypesUmbrella(
-        val int: Int,
-        val long: Long,
-        val float: Float,
-        val double: Double,
-        val boolean: Boolean,
-        val char: Char
+    val int: Int,
+    val long: Long,
+    val float: Float,
+    val double: Double,
+    val boolean: Boolean,
+    val char: Char
 )
 
 @Serializable
@@ -91,6 +91,29 @@
     }
 }
 
+@Serializable
+data class NullableByteStringDefaultNull(
+    @ByteString val byteString: ByteArray ? = null
+) {
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (other == null || this::class != other::class) return false
+
+        other as NullableByteString
+
+        if (byteString != null) {
+            if (other.byteString == null) return false
+            if (!byteString.contentEquals(other.byteString)) return false
+        } else if (other.byteString != null) return false
+
+        return true
+    }
+
+    override fun hashCode(): Int {
+        return byteString?.contentHashCode() ?: 0
+    }
+}
+
 @Serializable(with = CustomByteStringSerializer::class)
 data class CustomByteString(val a: Byte, val b: Byte, val c: Byte)
 
diff --git a/formats/cbor/jvmTest/src/kotlinx/serialization/cbor/CborWriterSpecTest.kt b/formats/cbor/jvmTest/src/kotlinx/serialization/cbor/CborWriterSpecTest.kt
index 364cd67..41ba581 100644
--- a/formats/cbor/jvmTest/src/kotlinx/serialization/cbor/CborWriterSpecTest.kt
+++ b/formats/cbor/jvmTest/src/kotlinx/serialization/cbor/CborWriterSpecTest.kt
@@ -13,9 +13,9 @@
 class CborWriterSpecTest : WordSpec() {
     init {
 
-        fun withEncoder(block: CborEncoder.() -> Unit): String {
+        fun withEncoder(block: ByteArrayOutput.() -> Unit): String {
             val result = ByteArrayOutput()
-            CborEncoder(result).block()
+            result.block()
             return HexConverter.printHexBinary(result.toByteArray()).lowercase()
         }
 
diff --git a/formats/hocon/build.gradle b/formats/hocon/build.gradle
deleted file mode 100644
index c5fae36..0000000
--- a/formats/hocon/build.gradle
+++ /dev/null
@@ -1,44 +0,0 @@
-import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
-
-/*
- * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-apply plugin: 'kotlin'
-apply plugin: 'kotlinx-serialization'
-
-compileKotlin {
-    kotlinOptions {
-        allWarningsAsErrors = true
-        jvmTarget = '1.8'
-    }
-}
-
-tasks.withType(KotlinCompile).configureEach {
-    kotlinOptions {
-        if (rootProject.ext.kotlin_lv_override != null) {
-            languageVersion = rootProject.ext.kotlin_lv_override
-            freeCompilerArgs += "-Xsuppress-version-warnings"
-        }
-    }
-}
-
-java {
-    sourceCompatibility = JavaVersion.VERSION_1_8
-    targetCompatibility = JavaVersion.VERSION_1_8
-}
-
-
-dependencies {
-    api project(':kotlinx-serialization-core')
-    api 'org.jetbrains.kotlin:kotlin-stdlib'
-    api 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
-
-    api 'com.typesafe:config:1.4.1'
-
-    testImplementation 'org.jetbrains.kotlin:kotlin-test'
-    testImplementation 'junit:junit:4.12'
-}
-
-Java9Modularity.configureJava9ModuleInfo(project)
diff --git a/formats/hocon/build.gradle.kts b/formats/hocon/build.gradle.kts
new file mode 100644
index 0000000..e267052
--- /dev/null
+++ b/formats/hocon/build.gradle.kts
@@ -0,0 +1,59 @@
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+import Java9Modularity.configureJava9ModuleInfo
+import org.jetbrains.kotlin.gradle.dsl.*
+
+
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+plugins {
+    kotlin("jvm")
+    alias(libs.plugins.serialization)
+}
+
+kotlin {
+    jvmToolchain(jdkToolchainVersion)
+
+    compilerOptions {
+        jvmTarget = JvmTarget.JVM_1_8
+        if (overriddenLanguageVersion != null) {
+            languageVersion = KotlinVersion.fromVersion(overriddenLanguageVersion!!)
+            freeCompilerArgs.add("-Xsuppress-version-warnings")
+        }
+        freeCompilerArgs.add("-Xjdk-release=1.8")
+    }
+
+    sourceSets.all {
+        languageSettings {
+            progressiveMode = true
+
+            optIn("kotlinx.serialization.InternalSerializationApi")
+        }
+    }
+}
+
+// Only main
+tasks.compileKotlin {
+    compilerOptions {
+        allWarningsAsErrors = true
+    }
+}
+
+java {
+    sourceCompatibility = JavaVersion.VERSION_1_8
+    targetCompatibility = JavaVersion.VERSION_1_8
+}
+
+dependencies {
+    api(project(":kotlinx-serialization-core"))
+    api("org.jetbrains.kotlin:kotlin-stdlib")
+    api("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
+
+    api(libs.typesafe.config)
+
+    testImplementation("org.jetbrains.kotlin:kotlin-test")
+    testImplementation(libs.junit.junit4)
+}
+
+configureJava9ModuleInfo()
diff --git a/formats/json-io/api/kotlinx-serialization-json-io.api b/formats/json-io/api/kotlinx-serialization-json-io.api
new file mode 100644
index 0000000..4d626a2
--- /dev/null
+++ b/formats/json-io/api/kotlinx-serialization-json-io.api
@@ -0,0 +1,7 @@
+public final class kotlinx/serialization/json/io/IoStreamsKt {
+	public static final fun decodeFromSource (Lkotlinx/serialization/json/Json;Lkotlinx/serialization/DeserializationStrategy;Lkotlinx/io/Source;)Ljava/lang/Object;
+	public static final fun decodeSourceToSequence (Lkotlinx/serialization/json/Json;Lkotlinx/io/Source;Lkotlinx/serialization/DeserializationStrategy;Lkotlinx/serialization/json/DecodeSequenceMode;)Lkotlin/sequences/Sequence;
+	public static synthetic fun decodeSourceToSequence$default (Lkotlinx/serialization/json/Json;Lkotlinx/io/Source;Lkotlinx/serialization/DeserializationStrategy;Lkotlinx/serialization/json/DecodeSequenceMode;ILjava/lang/Object;)Lkotlin/sequences/Sequence;
+	public static final fun encodeToSink (Lkotlinx/serialization/json/Json;Lkotlinx/serialization/SerializationStrategy;Ljava/lang/Object;Lkotlinx/io/Sink;)V
+}
+
diff --git a/formats/json-io/api/kotlinx-serialization-json-io.klib.api b/formats/json-io/api/kotlinx-serialization-json-io.klib.api
new file mode 100644
index 0000000..2a02575
--- /dev/null
+++ b/formats/json-io/api/kotlinx-serialization-json-io.klib.api
@@ -0,0 +1,14 @@
+// Klib ABI Dump
+// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, js, linuxArm32Hfp, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, wasmJs, wasmWasi, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64]
+// Rendering settings:
+// - Signature version: 2
+// - Show manifest properties: true
+// - Show declarations: true
+
+// Library unique name: <org.jetbrains.kotlinx:kotlinx-serialization-json-io>
+final fun <#A: kotlin/Any?> (kotlinx.serialization.json/Json).kotlinx.serialization.json.io/decodeFromSource(kotlinx.serialization/DeserializationStrategy<#A>, kotlinx.io/Source): #A // kotlinx.serialization.json.io/decodeFromSource|[email protected](kotlinx.serialization.DeserializationStrategy<0:0>;kotlinx.io.Source){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.serialization.json/Json).kotlinx.serialization.json.io/decodeSourceToSequence(kotlinx.io/Source, kotlinx.serialization/DeserializationStrategy<#A>, kotlinx.serialization.json/DecodeSequenceMode = ...): kotlin.sequences/Sequence<#A> // kotlinx.serialization.json.io/decodeSourceToSequence|[email protected](kotlinx.io.Source;kotlinx.serialization.DeserializationStrategy<0:0>;kotlinx.serialization.json.DecodeSequenceMode){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.serialization.json/Json).kotlinx.serialization.json.io/encodeToSink(kotlinx.serialization/SerializationStrategy<#A>, #A, kotlinx.io/Sink) // kotlinx.serialization.json.io/encodeToSink|[email protected](kotlinx.serialization.SerializationStrategy<0:0>;0:0;kotlinx.io.Sink){0§<kotlin.Any?>}[0]
+final inline fun <#A: reified kotlin/Any?> (kotlinx.serialization.json/Json).kotlinx.serialization.json.io/decodeFromSource(kotlinx.io/Source): #A // kotlinx.serialization.json.io/decodeFromSource|[email protected](kotlinx.io.Source){0§<kotlin.Any?>}[0]
+final inline fun <#A: reified kotlin/Any?> (kotlinx.serialization.json/Json).kotlinx.serialization.json.io/decodeSourceToSequence(kotlinx.io/Source, kotlinx.serialization.json/DecodeSequenceMode = ...): kotlin.sequences/Sequence<#A> // kotlinx.serialization.json.io/decodeSourceToSequence|[email protected](kotlinx.io.Source;kotlinx.serialization.json.DecodeSequenceMode){0§<kotlin.Any?>}[0]
+final inline fun <#A: reified kotlin/Any?> (kotlinx.serialization.json/Json).kotlinx.serialization.json.io/encodeToSink(#A, kotlinx.io/Sink) // kotlinx.serialization.json.io/encodeToSink|[email protected](0:0;kotlinx.io.Sink){0§<kotlin.Any?>}[0]
diff --git a/formats/json-io/build.gradle.kts b/formats/json-io/build.gradle.kts
new file mode 100644
index 0000000..b7f3921
--- /dev/null
+++ b/formats/json-io/build.gradle.kts
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+import Java9Modularity.configureJava9ModuleInfo
+import org.jetbrains.dokka.gradle.*
+import java.net.*
+
+plugins {
+    kotlin("multiplatform")
+    kotlin("plugin.serialization")
+
+    id("native-targets-conventions")
+    id("source-sets-conventions")
+}
+
+kotlin {
+    sourceSets {
+        configureEach {
+            languageSettings {
+                optIn("kotlinx.serialization.internal.CoreFriendModuleApi")
+                optIn("kotlinx.serialization.json.internal.JsonFriendModuleApi")
+            }
+        }
+        val commonMain by getting {
+            dependencies {
+                api(project(":kotlinx-serialization-core"))
+                api(project(":kotlinx-serialization-json"))
+                implementation(libs.kotlinx.io)
+            }
+        }
+    }
+}
+
+project.configureJava9ModuleInfo()
+
+tasks.named<DokkaTaskPartial>("dokkaHtmlPartial") {
+    dokkaSourceSets {
+        configureEach {
+            externalDocumentationLink {
+                url.set(URL("https://kotlin.github.io/kotlinx-io/"))
+            }
+        }
+    }
+}
diff --git a/formats/json-io/commonMain/src/kotlinx/serialization/json/io/IoStreams.kt b/formats/json-io/commonMain/src/kotlinx/serialization/json/io/IoStreams.kt
new file mode 100644
index 0000000..2b42db8
--- /dev/null
+++ b/formats/json-io/commonMain/src/kotlinx/serialization/json/io/IoStreams.kt
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.io
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.DecodeSequenceMode
+import kotlinx.serialization.json.Json
+import kotlinx.serialization.json.internal.*
+import kotlinx.serialization.json.io.internal.JsonToIoStreamWriter
+import kotlinx.serialization.json.internal.decodeToSequenceByReader
+import kotlinx.serialization.json.io.internal.IoSerialReader
+import kotlinx.io.*
+
+/**
+ * Serializes the [value] with [serializer] into a [sink] using JSON format and UTF-8 encoding.
+ *
+ * @throws [SerializationException] if the given value cannot be serialized to JSON.
+ * @throws [kotlinx.io.IOException] If an I/O error occurs and sink can't be written to.
+ */
+@ExperimentalSerializationApi
+public fun <T> Json.encodeToSink(
+    serializer: SerializationStrategy<T>,
+    value: T,
+    sink: Sink
+) {
+    val writer = JsonToIoStreamWriter(sink)
+    try {
+        encodeByWriter(this, writer, serializer, value)
+    } finally {
+        writer.release()
+    }
+}
+
+/**
+ * Serializes given [value] to a [sink] using UTF-8 encoding and serializer retrieved from the reified type parameter.
+ *
+ * @throws [SerializationException] if the given value cannot be serialized to JSON.
+ * @throws [kotlinx.io.IOException] If an I/O error occurs and sink can't be written to.
+ */
+@ExperimentalSerializationApi
+public inline fun <reified T> Json.encodeToSink(
+    value: T,
+    sink: Sink
+): Unit = encodeToSink(serializersModule.serializer(), value, sink)
+
+
+/**
+ * Deserializes JSON from [source] using UTF-8 encoding to a value of type [T] using [deserializer].
+ *
+ * Note that this functions expects that exactly one object would be present in the source
+ * and throws an exception if there are any dangling bytes after an object.
+ *
+ * @throws [SerializationException] if the given JSON input cannot be deserialized to the value of type [T].
+ * @throws [kotlinx.io.IOException] If an I/O error occurs and source can't be read from.
+ */
+@ExperimentalSerializationApi
+public fun <T> Json.decodeFromSource(
+    deserializer: DeserializationStrategy<T>,
+    source: Source
+): T {
+    return decodeByReader(this, deserializer, IoSerialReader(source))
+}
+
+/**
+ * Deserializes the contents of given [source] to the value of type [T] using UTF-8 encoding and
+ * deserializer retrieved from the reified type parameter.
+ *
+ * Note that this functions expects that exactly one object would be present in the stream
+ * and throws an exception if there are any dangling bytes after an object.
+ *
+ * @throws [SerializationException] if the given JSON input cannot be deserialized to the value of type [T].
+ * @throws [kotlinx.io.IOException] If an I/O error occurs and source can't be read from.
+ */
+@ExperimentalSerializationApi
+public inline fun <reified T> Json.decodeFromSource(source: Source): T =
+    decodeFromSource(serializersModule.serializer(), source)
+
+
+/**
+ * Transforms the given [source] into lazily deserialized sequence of elements of type [T] using UTF-8 encoding and [deserializer].
+ * Unlike [decodeFromSource], [source] is allowed to have more than one element, separated as [format] declares.
+ *
+ * Elements must all be of type [T].
+ * Elements are parsed lazily when resulting [Sequence] is evaluated.
+ * Resulting sequence is tied to the stream and can be evaluated only once.
+ *
+ * **Resource caution:** this method neither closes the [source] when the parsing is finished nor provides a method to close it manually.
+ * It is a caller responsibility to hold a reference to a source and close it. Moreover, because source is parsed lazily,
+ * closing it before returned sequence is evaluated completely will result in [Exception] from decoder.
+ *
+ * @throws [SerializationException] if the given JSON input cannot be deserialized to the value of type [T].
+ * @throws [kotlinx.io.IOException] If an I/O error occurs and source can't be read from.
+ */
+@ExperimentalSerializationApi
+public fun <T> Json.decodeSourceToSequence(
+    source: Source,
+    deserializer: DeserializationStrategy<T>,
+    format: DecodeSequenceMode = DecodeSequenceMode.AUTO_DETECT
+): Sequence<T> {
+    return decodeToSequenceByReader(this, IoSerialReader(source), deserializer, format)
+}
+
+/**
+ * Transforms the given [source] into lazily deserialized sequence of elements of type [T] using UTF-8 encoding and deserializer retrieved from the reified type parameter.
+ * Unlike [decodeSourceToSequence], [source] is allowed to have more than one element, separated as [format] declares.
+ *
+ * Elements must all be of type [T].
+ * Elements are parsed lazily when resulting [Sequence] is evaluated.
+ * Resulting sequence is tied to the stream and constrained to be evaluated only once.
+ *
+ * **Resource caution:** this method does not close [source] when the parsing is finished neither provides method to close it manually.
+ * It is a caller responsibility to hold a reference to a source and close it. Moreover, because source is parsed lazily,
+ * closing it before returned sequence is evaluated fully would result in [Exception] from decoder.
+ *
+ * @throws [SerializationException] if the given JSON input cannot be deserialized to the value of type [T].
+ * @throws [kotlinx.io.IOException] If an I/O error occurs and source can't be read from.
+ */
+@ExperimentalSerializationApi
+public inline fun <reified T> Json.decodeSourceToSequence(
+    source: Source,
+    format: DecodeSequenceMode = DecodeSequenceMode.AUTO_DETECT
+): Sequence<T> = decodeSourceToSequence(source, serializersModule.serializer(), format)
diff --git a/formats/json-io/commonMain/src/kotlinx/serialization/json/io/internal/IoJsonStreams.kt b/formats/json-io/commonMain/src/kotlinx/serialization/json/io/internal/IoJsonStreams.kt
new file mode 100644
index 0000000..fc39aff
--- /dev/null
+++ b/formats/json-io/commonMain/src/kotlinx/serialization/json/io/internal/IoJsonStreams.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.io.internal
+
+import kotlinx.io.*
+import kotlinx.serialization.json.internal.*
+
+private const val QUOTE_CODE = '"'.code
+
+internal class JsonToIoStreamWriter(private val sink: Sink) : InternalJsonWriter {
+
+    override fun writeLong(value: Long) {
+        write(value.toString())
+    }
+
+    override fun writeChar(char: Char) {
+        sink.writeCodePointValue(char.code)
+    }
+
+    override fun write(text: String) {
+        sink.writeString(text)
+    }
+
+    override fun writeQuoted(text: String) {
+        sink.writeCodePointValue(QUOTE_CODE)
+        InternalJsonWriter.doWriteEscaping(text) { s, start, end -> sink.writeString(s, start, end) }
+        sink.writeCodePointValue(QUOTE_CODE)
+    }
+
+    override fun release() {
+        // no-op, see https://github.com/Kotlin/kotlinx.serialization/pull/1982#discussion_r915043700
+    }
+}
+
+internal class IoSerialReader(private val source: Source): InternalJsonReaderCodePointImpl() {
+    override fun exhausted(): Boolean = source.exhausted()
+    override fun nextCodePoint(): Int = source.readCodePointValue()
+}
diff --git a/formats/json-io/commonTest/src/kotlinx/serialization/json/io/IoTests.kt b/formats/json-io/commonTest/src/kotlinx/serialization/json/io/IoTests.kt
new file mode 100644
index 0000000..cd1551d
--- /dev/null
+++ b/formats/json-io/commonTest/src/kotlinx/serialization/json/io/IoTests.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.io
+
+import kotlinx.io.*
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.json.*
+import kotlinx.serialization.json.io.internal.*
+import kotlin.test.*
+
+class IoTests {
+
+    @Serializable
+    data class Simple(val i: Int)
+
+    @Test
+    fun testSurrogate() {
+        val text = "\uD83D\uDE03"
+        val originalChars = text.toCharArray()
+
+        val buffer = Buffer()
+        buffer.writeString(text)
+        val reader = IoSerialReader(buffer)
+
+        val readArray = CharArray(2)
+        assertEquals(1, reader.read(readArray, 0, 1) )
+        assertEquals(1, reader.read(readArray, 1, 1) )
+
+        assertContentEquals(originalChars, readArray)
+    }
+
+
+    @Test
+    fun testEncodingAndDecoding() {
+        val json = "{\"i\":42}"
+        val value = Simple(42)
+        val buffer = Buffer()
+        Json.encodeToSink(value, buffer)
+        val encoded = buffer.readString()
+        assertEquals(json, encoded)
+
+        buffer.writeString(encoded)
+        val decoded = Json.decodeFromSource<Simple>(buffer)
+        assertEquals(value, decoded)
+
+        assertTrue(buffer.exhausted())
+    }
+
+    @Test
+    fun testDecodeSequence() {
+        val json = "{\"i\":1}{\"i\":2}"
+        val value1 = Simple(1)
+        val value2 = Simple(2)
+        val buffer = Buffer()
+        buffer.writeString(json)
+        val decoded = Json.decodeSourceToSequence<Simple>(buffer).toList()
+
+        assertTrue(buffer.exhausted())
+        assertEquals(2, decoded.size)
+        assertEquals(listOf(value1, value2), decoded)
+
+        buffer.writeString(json)
+        val decodedExplicit = Json.decodeSourceToSequence(buffer, Simple.serializer()).toList()
+        assertTrue(buffer.exhausted())
+        assertEquals(2, decodedExplicit.size)
+        assertEquals(listOf(value1, value2), decodedExplicit)
+    }
+}
\ No newline at end of file
diff --git a/formats/json-okio/api/kotlinx-serialization-json-okio.klib.api b/formats/json-okio/api/kotlinx-serialization-json-okio.klib.api
new file mode 100644
index 0000000..dd78917
--- /dev/null
+++ b/formats/json-okio/api/kotlinx-serialization-json-okio.klib.api
@@ -0,0 +1,14 @@
+// Klib ABI Dump
+// Targets: [iosArm64, iosSimulatorArm64, iosX64, js, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, wasmJs, wasmWasi, watchosArm32, watchosArm64, watchosSimulatorArm64, watchosX64]
+// Rendering settings:
+// - Signature version: 2
+// - Show manifest properties: true
+// - Show declarations: true
+
+// Library unique name: <org.jetbrains.kotlinx:kotlinx-serialization-json-okio>
+final fun <#A: kotlin/Any?> (kotlinx.serialization.json/Json).kotlinx.serialization.json.okio/decodeBufferedSourceToSequence(okio/BufferedSource, kotlinx.serialization/DeserializationStrategy<#A>, kotlinx.serialization.json/DecodeSequenceMode = ...): kotlin.sequences/Sequence<#A> // kotlinx.serialization.json.okio/decodeBufferedSourceToSequence|[email protected](okio.BufferedSource;kotlinx.serialization.DeserializationStrategy<0:0>;kotlinx.serialization.json.DecodeSequenceMode){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.serialization.json/Json).kotlinx.serialization.json.okio/decodeFromBufferedSource(kotlinx.serialization/DeserializationStrategy<#A>, okio/BufferedSource): #A // kotlinx.serialization.json.okio/decodeFromBufferedSource|[email protected](kotlinx.serialization.DeserializationStrategy<0:0>;okio.BufferedSource){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> (kotlinx.serialization.json/Json).kotlinx.serialization.json.okio/encodeToBufferedSink(kotlinx.serialization/SerializationStrategy<#A>, #A, okio/BufferedSink) // kotlinx.serialization.json.okio/encodeToBufferedSink|[email protected](kotlinx.serialization.SerializationStrategy<0:0>;0:0;okio.BufferedSink){0§<kotlin.Any?>}[0]
+final inline fun <#A: reified kotlin/Any?> (kotlinx.serialization.json/Json).kotlinx.serialization.json.okio/decodeBufferedSourceToSequence(okio/BufferedSource, kotlinx.serialization.json/DecodeSequenceMode = ...): kotlin.sequences/Sequence<#A> // kotlinx.serialization.json.okio/decodeBufferedSourceToSequence|[email protected](okio.BufferedSource;kotlinx.serialization.json.DecodeSequenceMode){0§<kotlin.Any?>}[0]
+final inline fun <#A: reified kotlin/Any?> (kotlinx.serialization.json/Json).kotlinx.serialization.json.okio/decodeFromBufferedSource(okio/BufferedSource): #A // kotlinx.serialization.json.okio/decodeFromBufferedSource|[email protected](okio.BufferedSource){0§<kotlin.Any?>}[0]
+final inline fun <#A: reified kotlin/Any?> (kotlinx.serialization.json/Json).kotlinx.serialization.json.okio/encodeToBufferedSink(#A, okio/BufferedSink) // kotlinx.serialization.json.okio/encodeToBufferedSink|[email protected](0:0;okio.BufferedSink){0§<kotlin.Any?>}[0]
diff --git a/formats/json-okio/build.gradle.kts b/formats/json-okio/build.gradle.kts
index a51fff0..6e49940 100644
--- a/formats/json-okio/build.gradle.kts
+++ b/formats/json-okio/build.gradle.kts
@@ -7,11 +7,12 @@
 
 plugins {
     kotlin("multiplatform")
-    kotlin("plugin.serialization")
+    alias(libs.plugins.serialization)
+
+    id("native-targets-conventions")
+    id("source-sets-conventions")
 }
 
-apply(from = rootProject.file("gradle/native-targets.gradle"))
-apply(from = rootProject.file("gradle/configure-source-sets.gradle"))
 
 kotlin {
     sourceSets {
@@ -25,12 +26,7 @@
             dependencies {
                 api(project(":kotlinx-serialization-core"))
                 api(project(":kotlinx-serialization-json"))
-                implementation("com.squareup.okio:okio:${property("okio_version")}")
-            }
-        }
-        val commonTest by getting {
-            dependencies {
-                implementation("com.squareup.okio:okio:${property("okio_version")}")
+                implementation(libs.okio)
             }
         }
     }
@@ -50,13 +46,3 @@
         }
     }
 }
-
-
-// TODO: Remove this after okio will be updated to the version with 1.9.20 stdlib dependency
-configurations.all {
-    resolutionStrategy.eachDependency {
-        if (requested.name == "kotlin-stdlib-wasm") {
-            useTarget("org.jetbrains.kotlin:kotlin-stdlib-wasm-js:${requested.version}")
-        }
-    }
-}
diff --git a/formats/json-okio/commonMain/src/kotlinx/serialization/json/okio/internal/OkioJsonStreams.kt b/formats/json-okio/commonMain/src/kotlinx/serialization/json/okio/internal/OkioJsonStreams.kt
index 1de8971..a4e433b 100644
--- a/formats/json-okio/commonMain/src/kotlinx/serialization/json/okio/internal/OkioJsonStreams.kt
+++ b/formats/json-okio/commonMain/src/kotlinx/serialization/json/okio/internal/OkioJsonStreams.kt
@@ -7,32 +7,7 @@
 import kotlinx.serialization.json.internal.*
 import okio.*
 
-// Copied from kotlinx/serialization/json/internal/StringOps.kt
-private fun toHexChar(i: Int) : Char {
-    val d = i and 0xf
-    return if (d < 10) (d + '0'.code).toChar()
-    else (d - 10 + 'a'.code).toChar()
-}
-
-// Copied from kotlinx/serialization/json/internal/StringOps.kt
-private val ESCAPE_STRINGS: Array<String?> = arrayOfNulls<String>(93).apply {
-    for (c in 0..0x1f) {
-        val c1 = toHexChar(c shr 12)
-        val c2 = toHexChar(c shr 8)
-        val c3 = toHexChar(c shr 4)
-        val c4 = toHexChar(c)
-        this[c] = "\\u$c1$c2$c3$c4"
-    }
-    this['"'.code] = "\\\""
-    this['\\'.code] = "\\\\"
-    this['\t'.code] = "\\t"
-    this['\b'.code] = "\\b"
-    this['\n'.code] = "\\n"
-    this['\r'.code] = "\\r"
-    this[0x0c] = "\\f"
-}
-
-
+private const val QUOTE_CODE = '"'.code
 
 internal class JsonToOkioStreamWriter(private val sink: BufferedSink) : InternalJsonWriter {
     override fun writeLong(value: Long) {
@@ -48,20 +23,9 @@
     }
 
     override fun writeQuoted(text: String) {
-        sink.writeUtf8CodePoint('"'.code)
-        var lastPos = 0
-        for (i in text.indices) {
-            val c = text[i].code
-            if (c < ESCAPE_STRINGS.size && ESCAPE_STRINGS[c] != null) {
-                sink.writeUtf8(text, lastPos, i) // flush prev
-                sink.writeUtf8(ESCAPE_STRINGS[c]!!)
-                lastPos = i + 1
-            }
-        }
-
-        if (lastPos != 0) sink.writeUtf8(text, lastPos, text.length)
-        else sink.writeUtf8(text)
-        sink.writeUtf8CodePoint('"'.code)
+        sink.writeUtf8CodePoint(QUOTE_CODE)
+        InternalJsonWriter.doWriteEscaping(text) { s, start, end -> sink.writeUtf8(s, start, end) }
+        sink.writeUtf8CodePoint(QUOTE_CODE)
     }
 
     override fun release() {
@@ -69,56 +33,8 @@
     }
 }
 
-// Max value for a code  point placed in one Char
-private const val SINGLE_CHAR_MAX_CODEPOINT = Char.MAX_VALUE.code
-// Value added to the high UTF-16 surrogate after shifting
-private const val HIGH_SURROGATE_HEADER = 0xd800 - (0x010000 ushr 10)
-// Value added to the low UTF-16 surrogate after masking
-private const val LOW_SURROGATE_HEADER = 0xdc00
-
-
-internal class OkioSerialReader(private val source: BufferedSource): InternalJsonReader {
-    /*
-    A sequence of code points is read from UTF-8, some of it can take 2 characters.
-    In case the last code point requires 2 characters, and the array is already full, we buffer the second character
-     */
-    private var bufferedChar: Char? = null
-
-    override fun read(buffer: CharArray, bufferOffset: Int, count: Int): Int {
-        var i = 0
-
-        if (bufferedChar != null) {
-            buffer[bufferOffset + i] = bufferedChar!!
-            i++
-            bufferedChar = null
-        }
-
-        while (i < count && !source.exhausted()) {
-            val codePoint = source.readUtf8CodePoint()
-            if (codePoint <= SINGLE_CHAR_MAX_CODEPOINT) {
-                buffer[bufferOffset + i] = codePoint.toChar()
-                i++
-            } else {
-                // an example of working with surrogates is taken from okio library with minor changes, see https://github.com/square/okio
-                // UTF-16 high surrogate: 110110xxxxxxxxxx (10 bits)
-                // UTF-16 low surrogate:  110111yyyyyyyyyy (10 bits)
-                // Unicode code point:    00010000000000000000 + xxxxxxxxxxyyyyyyyyyy (21 bits)
-                val upChar = ((codePoint ushr 10) + HIGH_SURROGATE_HEADER).toChar()
-                val lowChar = ((codePoint and 0x03ff) + LOW_SURROGATE_HEADER).toChar()
-
-                buffer[bufferOffset + i] = upChar
-                i++
-
-                if (i < count) {
-                    buffer[bufferOffset + i] = lowChar
-                    i++
-                } else {
-                        // if char array is full - buffer lower surrogate
-                    bufferedChar = lowChar
-                }
-            }
-        }
-        return if (i > 0) i else -1
-    }
+internal class OkioSerialReader(private val source: BufferedSource): InternalJsonReaderCodePointImpl() {
+    override fun exhausted(): Boolean = source.exhausted()
+    override fun nextCodePoint(): Int = source.readUtf8CodePoint()
 }
 
diff --git a/formats/json-okio/commonTest/src/kotlinx/serialization/json/okio/internal/OkioTests.kt b/formats/json-okio/commonTest/src/kotlinx/serialization/json/okio/internal/OkioTests.kt
new file mode 100644
index 0000000..93df4a2
--- /dev/null
+++ b/formats/json-okio/commonTest/src/kotlinx/serialization/json/okio/internal/OkioTests.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.okio.internal
+
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.json.*
+import kotlinx.serialization.json.okio.*
+import okio.*
+import kotlin.test.*
+
+class OkioTests {
+
+    @Serializable
+    data class Simple(val i: Int)
+
+    @Test
+    fun testSurrogate() {
+        val text = "\uD83D\uDE03"
+        val originalChars = text.toCharArray()
+
+        val buffer = Buffer()
+        buffer.writeUtf8(text)
+        val reader = OkioSerialReader(buffer)
+
+        val readArray = CharArray(2)
+        assertEquals(1, reader.read(readArray, 0, 1) )
+        assertEquals(1, reader.read(readArray, 1, 1) )
+
+        assertContentEquals(originalChars, readArray)
+    }
+
+
+    @Test
+    fun testEncodingAndDecoding() {
+        val json = "{\"i\":42}"
+        val value = Simple(42)
+        val buffer = Buffer()
+        Json.encodeToBufferedSink(value, buffer)
+        val encoded = buffer.readUtf8()
+        assertEquals(json, encoded)
+
+        buffer.writeUtf8(encoded)
+        val decoded = Json.decodeFromBufferedSource<Simple>(buffer)
+        assertEquals(value, decoded)
+
+        assertTrue(buffer.exhausted())
+    }
+
+    @Test
+    fun testDecodeSequence() {
+        val json = "{\"i\":1}{\"i\":2}"
+        val value1 = Simple(1)
+        val value2 = Simple(2)
+        val buffer = Buffer()
+        buffer.writeUtf8(json)
+        val decoded = Json.decodeBufferedSourceToSequence<Simple>(buffer).toList()
+
+        assertTrue(buffer.exhausted())
+        assertEquals(2, decoded.size)
+        assertEquals(listOf(value1, value2), decoded)
+
+        buffer.writeUtf8(json)
+        val decodedExplicit = Json.decodeBufferedSourceToSequence(buffer, Simple.serializer()).toList()
+        assertTrue(buffer.exhausted())
+        assertEquals(2, decodedExplicit.size)
+        assertEquals(listOf(value1, value2), decodedExplicit)
+    }
+}
\ No newline at end of file
diff --git a/formats/json-tests/build.gradle.kts b/formats/json-tests/build.gradle.kts
index 6be0a3a..cd8ef12 100644
--- a/formats/json-tests/build.gradle.kts
+++ b/formats/json-tests/build.gradle.kts
@@ -2,15 +2,14 @@
  * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
  */
 import Java9Modularity.configureJava9ModuleInfo
-import org.jetbrains.kotlin.gradle.targets.js.testing.*
 
 plugins {
     kotlin("multiplatform")
-    kotlin("plugin.serialization")
-}
+    alias(libs.plugins.serialization)
 
-apply(from = rootProject.file("gradle/native-targets.gradle"))
-apply(from = rootProject.file("gradle/configure-source-sets.gradle"))
+    id("native-targets-conventions")
+    id("source-sets-conventions")
+}
 
 // disable kover tasks because there are no non-test classes in the project
 tasks.named("koverHtmlReport") {
@@ -27,6 +26,7 @@
     sourceSets {
         configureEach {
             languageSettings {
+                optIn("kotlin.uuid.ExperimentalUuidApi")
                 optIn("kotlinx.serialization.internal.CoreFriendModuleApi")
                 optIn("kotlinx.serialization.json.internal.JsonFriendModuleApi")
             }
@@ -35,26 +35,19 @@
             dependencies {
                 api(project(":kotlinx-serialization-json"))
                 api(project(":kotlinx-serialization-json-okio"))
-                implementation("com.squareup.okio:okio:${property("okio_version")}")
+                api(project(":kotlinx-serialization-json-io"))
+                implementation(libs.kotlinx.io)
+                implementation(libs.okio)
             }
         }
 
         val jvmTest by getting {
             dependencies {
-                implementation("com.google.code.gson:gson:2.8.5")
-                implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:${property("coroutines_version")}")
+                implementation(libs.gson)
+                implementation(libs.coroutines.core)
             }
         }
     }
 }
 
 project.configureJava9ModuleInfo()
-
-// TODO: Remove this after okio will be updated to the version with 1.9.20 stdlib dependency
-configurations.all {
-    resolutionStrategy.eachDependency {
-        if (requested.name == "kotlin-stdlib-wasm") {
-            useTarget("org.jetbrains.kotlin:kotlin-stdlib-wasm-js:${requested.version}")
-        }
-    }
-}
diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/JsonElementPolymorphicErrorTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/JsonElementPolymorphicErrorTest.kt
new file mode 100644
index 0000000..ee29049
--- /dev/null
+++ b/formats/json-tests/commonTest/src/kotlinx/serialization/JsonElementPolymorphicErrorTest.kt
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+
+import kotlinx.serialization.json.*
+import kotlinx.serialization.modules.SerializersModule
+import kotlinx.serialization.modules.polymorphic
+import kotlinx.serialization.test.*
+import kotlin.test.*
+
+class JsonElementPolymorphicErrorTest : JsonTestBase() {
+
+    @Serializable
+    abstract class Abstract
+
+    @Serializable
+    data class IntChild(val value: Int) : Abstract()
+
+    @Serializable
+    data class CollectionChild(val value: Int) : Abstract()
+
+    @Serializable
+    data class Holder(val value: Abstract)
+
+    private val format = Json {
+        prettyPrint = false
+        serializersModule = SerializersModule {
+            polymorphic(Abstract::class) {
+                subclass(IntChild::class, IntChildSerializer)
+                subclass(CollectionChild::class, CollectionChildSerializer)
+            }
+        }
+    }
+
+    object IntChildSerializer : JsonTransformingSerializer<IntChild>(serializer()) {
+        override fun transformSerialize(element: JsonElement): JsonElement {
+            return element.jsonObject.getValue("value")
+        }
+    }
+
+    object CollectionChildSerializer : JsonTransformingSerializer<CollectionChild>(serializer()) {
+        override fun transformSerialize(element: JsonElement): JsonElement {
+            val value = element.jsonObject.getValue("value")
+            return JsonArray(listOf(value))
+        }
+    }
+
+    @Test
+    fun test() = parametrizedTest { mode ->
+        assertFailsWithMessage<SerializationException>("Class with serial name kotlinx.serialization.JsonElementPolymorphicErrorTest.IntChild cannot be serialized polymorphically because it is represented as JsonLiteral. Make sure that its JsonTransformingSerializer returns JsonObject, so class discriminator can be added to it") {
+            format.encodeToString(
+                Holder.serializer(),
+                Holder(IntChild(42)),
+                mode
+            )
+        }
+
+        assertFailsWithMessage<SerializationException>("Class with serial name kotlinx.serialization.JsonElementPolymorphicErrorTest.CollectionChild cannot be serialized polymorphically because it is represented as JsonArray. Make sure that its JsonTransformingSerializer returns JsonObject, so class discriminator can be added to it") {
+            format.encodeToString(
+                Holder.serializer(),
+                Holder(CollectionChild(42)),
+                mode
+            )
+        }
+
+    }
+
+}
diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/KeepGeneratedSerializerTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/KeepGeneratedSerializerTest.kt
new file mode 100644
index 0000000..0f87bc9
--- /dev/null
+++ b/formats/json-tests/commonTest/src/kotlinx/serialization/KeepGeneratedSerializerTest.kt
@@ -0,0 +1,247 @@
+/*
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.json.*
+import kotlin.jvm.*
+import kotlin.test.*
+
+class KeepGeneratedSerializerTest {
+    @Serializable(with = ValueSerializer::class)
+    @KeepGeneratedSerializer
+    @JvmInline
+    value class Value(val i: Int)
+
+    object ValueSerializer: KSerializer<Value> {
+        override val descriptor = PrimitiveSerialDescriptor("ValueSerializer", PrimitiveKind.INT)
+        override fun deserialize(decoder: Decoder): Value {
+            val value = decoder.decodeInt()
+            return Value(value - 42)
+        }
+        override fun serialize(encoder: Encoder, value: Value) {
+            encoder.encodeInt(value.i + 42)
+        }
+    }
+
+    @Test
+    fun testValueClass() {
+        test(Value(1), "43", "1", Value.serializer(), Value.generatedSerializer())
+    }
+
+
+
+    @Serializable(with = DataSerializer::class)
+    @KeepGeneratedSerializer
+    data class Data(val i: Int)
+
+    object DataSerializer: KSerializer<Data> {
+        override val descriptor = PrimitiveSerialDescriptor("DataSerializer", PrimitiveKind.INT)
+        override fun deserialize(decoder: Decoder): Data {
+            val value = decoder.decodeInt()
+            return Data(value)
+        }
+        override fun serialize(encoder: Encoder, value: Data) {
+            encoder.encodeInt(value.i)
+        }
+    }
+
+    @Test
+    fun testDataClass() {
+        test(Data(2), "2", "{\"i\":2}", Data.serializer(), Data.generatedSerializer())
+    }
+
+
+    @Serializable(with = ParentSerializer::class)
+    @KeepGeneratedSerializer
+    open class Parent(val p: Int) {
+        override fun equals(other: Any?): Boolean {
+            if (this === other) return true
+            if (other !is Parent) return false
+
+            if (p != other.p) return false
+
+            return true
+        }
+
+        override fun hashCode(): Int {
+            return p
+        }
+    }
+
+    object ParentSerializer: KSerializer<Parent> {
+        override val descriptor = PrimitiveSerialDescriptor("ParentSerializer", PrimitiveKind.INT)
+        override fun deserialize(decoder: Decoder): Parent {
+            val value = decoder.decodeInt()
+            return Parent(value - 1)
+        }
+        override fun serialize(encoder: Encoder, value: Parent) {
+            encoder.encodeInt(value.p + 1)
+        }
+    }
+
+    @Serializable
+    data class Child(val c: Int): Parent(0)
+
+    @Serializable(with = ChildSerializer::class)
+    @KeepGeneratedSerializer
+    data class ChildWithCustom(val c: Int): Parent(0)
+
+    object ChildSerializer: KSerializer<ChildWithCustom> {
+        override val descriptor = PrimitiveSerialDescriptor("ChildSerializer", PrimitiveKind.INT)
+        override fun deserialize(decoder: Decoder): ChildWithCustom {
+            val value = decoder.decodeInt()
+            return ChildWithCustom(value - 2)
+        }
+
+        override fun serialize(encoder: Encoder, value: ChildWithCustom) {
+            encoder.encodeInt(value.c + 2)
+        }
+    }
+
+    @Test
+    fun testInheritance() {
+        test(Parent(3), "4", "{\"p\":3}", Parent.serializer(), Parent.generatedSerializer())
+        test(Child(4), "{\"p\":0,\"c\":4}", "", Child.serializer(), null)
+        test(ChildWithCustom(5), "7", "{\"p\":0,\"c\":5}", ChildWithCustom.serializer(), ChildWithCustom.generatedSerializer())
+    }
+
+
+    @Serializable(with = MyEnumSerializer::class)
+    @KeepGeneratedSerializer
+    enum class MyEnum {
+        A,
+        B,
+        FALLBACK
+    }
+
+    @Serializable
+    data class EnumHolder(val e: MyEnum)
+
+    object MyEnumSerializer: KSerializer<MyEnum> {
+        val defaultSerializer = MyEnum.generatedSerializer()
+
+        override val descriptor = PrimitiveSerialDescriptor("MyEnumSerializer", PrimitiveKind.INT)
+
+        override fun deserialize(decoder: Decoder): MyEnum {
+            decoder.decodeString()
+            return MyEnum.A
+        }
+
+        override fun serialize(encoder: Encoder, value: MyEnum) {
+            // always encode FALLBACK entry by generated serializer
+            defaultSerializer.serialize(encoder, MyEnum.FALLBACK)
+        }
+    }
+
+    @Test
+    fun testEnum() {
+        test(MyEnum.A, "\"FALLBACK\"", "\"A\"", MyEnum.serializer(), MyEnum.generatedSerializer())
+        assertTrue(serializer<MyEnum>() is MyEnumSerializer, "serializer<MyEnum> illegal = " + serializer<MyEnum>())
+        assertTrue(MyEnum.serializer() is MyEnumSerializer, "MyEnum.serializer() illegal = " + MyEnum.serializer())
+        assertEquals("kotlinx.serialization.internal.EnumSerializer<kotlinx.serialization.KeepGeneratedSerializerTest.MyEnum>", MyEnum.generatedSerializer().toString(), "MyEnum.generatedSerializer() illegal")
+        assertSame(MyEnum.generatedSerializer(), MyEnum.generatedSerializer(), "MyEnum.generatedSerializer() instance differs")
+    }
+
+
+    @Serializable(with = ParametrizedSerializer::class)
+    @KeepGeneratedSerializer
+    data class ParametrizedData<T>(val t: T)
+
+    class ParametrizedSerializer(val serializer: KSerializer<Any>): KSerializer<ParametrizedData<Any>> {
+        override val descriptor = PrimitiveSerialDescriptor("ParametrizedSerializer", PrimitiveKind.INT)
+
+        override fun deserialize(decoder: Decoder): ParametrizedData<Any> {
+            val value = serializer.deserialize(decoder)
+            return ParametrizedData(value)
+        }
+
+        override fun serialize(encoder: Encoder, value: ParametrizedData<Any>) {
+            serializer.serialize(encoder, value.t)
+        }
+    }
+
+    @Test
+    fun testParametrized() {
+        test(
+            ParametrizedData<Data>(Data(6)), "6", "{\"t\":6}", ParametrizedData.serializer(Data.serializer()), ParametrizedData.generatedSerializer(
+                Data.serializer()))
+    }
+
+
+    @Serializable(WithCompanion.Companion::class)
+    @KeepGeneratedSerializer
+    data class WithCompanion(val value: Int) {
+        @Serializer(WithCompanion::class)
+        companion object {
+            override val descriptor = PrimitiveSerialDescriptor("WithCompanionDesc", PrimitiveKind.INT)
+            override fun deserialize(decoder: Decoder): WithCompanion {
+                val value = decoder.decodeInt()
+                return WithCompanion(value)
+            }
+
+            override fun serialize(encoder: Encoder, value: WithCompanion) {
+                encoder.encodeInt(value.value)
+            }
+        }
+    }
+
+    @Test
+    fun testCompanion() {
+        test(WithCompanion(7), "7", "{\"value\":7}", WithCompanion.serializer(), WithCompanion.generatedSerializer())
+    }
+
+
+    @Serializable(with = ObjectSerializer::class)
+    @KeepGeneratedSerializer
+    object Object
+
+    object ObjectSerializer: KSerializer<Object> {
+        override val descriptor = PrimitiveSerialDescriptor("ObjectSerializer", PrimitiveKind.INT)
+
+        override fun deserialize(decoder: Decoder): Object {
+            decoder.decodeInt()
+            return Object
+        }
+        override fun serialize(encoder: Encoder, value: Object) {
+            encoder.encodeInt(8)
+        }
+    }
+
+    @Test
+    fun testObject() {
+        test(Object, "8", "{}", Object.serializer(), Object.generatedSerializer())
+        assertEquals("kotlinx.serialization.KeepGeneratedSerializerTest.Object()", Object.generatedSerializer().descriptor.toString(), "Object.generatedSerializer() illegal")
+        assertSame(Object.generatedSerializer(), Object.generatedSerializer(), "Object.generatedSerializer() instance differs")
+    }
+
+
+
+    inline fun <reified T : Any> test(
+        value: T,
+        customJson: String,
+        keepJson: String,
+        serializer: KSerializer<T>,
+        generatedSerializer: KSerializer<T>?
+    ) {
+        val implicitJson = Json.encodeToString(value)
+        assertEquals(customJson, implicitJson, "Json.encodeToString(value: ${T::class.simpleName})")
+        val implicitDecoded = Json.decodeFromString<T>(implicitJson)
+        assertEquals(value, implicitDecoded, "Json.decodeFromString(json): ${T::class.simpleName}")
+
+        val exlicitJson = Json.encodeToString(serializer, value)
+        assertEquals(customJson, exlicitJson, "Json.encodeToString(${T::class.simpleName}.serializer(), value)")
+        val explicitDecoded = Json.decodeFromString(serializer, exlicitJson)
+        assertEquals(value, explicitDecoded, "Json.decodeFromString(${T::class.simpleName}.serializer(), json)")
+
+        if (generatedSerializer == null) return
+        val keep = Json.encodeToString(generatedSerializer, value)
+        assertEquals(keepJson, keep, "Json.encodeToString(${T::class.simpleName}.generatedSerializer(), value)")
+        val keepDecoded = Json.decodeFromString(generatedSerializer, keep)
+        assertEquals(value, keepDecoded, "Json.decodeFromString(${T::class.simpleName}.generatedSerializer(), json)")
+    }
+
+}
\ No newline at end of file
diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/SerializersLookupTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/SerializersLookupTest.kt
index 4b4aebf..f335d0d 100644
--- a/formats/json-tests/commonTest/src/kotlinx/serialization/SerializersLookupTest.kt
+++ b/formats/json-tests/commonTest/src/kotlinx/serialization/SerializersLookupTest.kt
@@ -15,6 +15,7 @@
 import kotlin.reflect.*
 import kotlin.test.*
 import kotlin.time.Duration
+import kotlin.uuid.*
 
 @Suppress("RemoveExplicitTypeArguments") // This is exactly what's being tested
 class SerializersLookupTest : JsonTestBase() {
@@ -142,6 +143,14 @@
     }
 
     @Test
+    @OptIn(ExperimentalUuidApi::class)
+    fun testLookupUuid() {
+        assertSame<KSerializer<*>?>(Uuid.serializer(), serializerOrNull(typeOf<Uuid>()))
+        // TODO: uncomment in 2.1 release
+//        assertSame<KSerializer<*>?>(Uuid.serializer(), serializer<Uuid>())
+    }
+
+    @Test
     fun testCustomGeneric() {
         val intBox = Box(42)
         val intBoxSerializer = serializer<Box<Int>>()
diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/ContextAndPolymorphicTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/ContextAndPolymorphicTest.kt
index ac24cf0..61f9238 100644
--- a/formats/json-tests/commonTest/src/kotlinx/serialization/features/ContextAndPolymorphicTest.kt
+++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/ContextAndPolymorphicTest.kt
@@ -129,9 +129,11 @@
         assertEquals("BinaryPayload", resolvedToBinary.serialName)
     }
 
+    private val jsonArrayWithDefaults = Json { useArrayPolymorphism = true; encodeDefaults = true }
+
     @Test
     fun testContextualSerializerUsesDefaultIfModuleIsEmpty() {
-        val s = Json { useArrayPolymorphism = true; encodeDefaults = true }.encodeToString(EnhancedData.serializer(), value)
+        val s = jsonArrayWithDefaults.encodeToString(EnhancedData.serializer(), value)
         assertEquals("""{"data":{"a":100500,"b":42},"stringPayload":{"s":"string"},"binaryPayload":"62696E617279"}""", s)
     }
 }
diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/JsonCommentsTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/JsonCommentsTest.kt
new file mode 100644
index 0000000..88c901d
--- /dev/null
+++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/JsonCommentsTest.kt
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.features
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlin.test.*
+
+class JsonCommentsTest: JsonTestBase() {
+    val json = Json(default) {
+        allowComments = true
+        allowTrailingComma = true
+    }
+
+    val withLenient = Json(json) {
+        isLenient = true
+        ignoreUnknownKeys = true
+    }
+
+    @Test
+    fun testBasic() = parametrizedTest { mode ->
+        val inputBlock = """{"data": "b" /*value b*/ }"""
+        val inputLine = "{\"data\": \"b\" // value b \n }"
+        assertEquals(StringData("b"), json.decodeFromString(inputBlock, mode))
+        assertEquals(StringData("b"), json.decodeFromString(inputLine, mode))
+    }
+
+    @Serializable
+    data class Target(val key: String, val key2: List<Int>, val key3: NestedTarget, val key4: String)
+
+    @Serializable
+    data class NestedTarget(val nestedKey: String)
+
+    private fun target(key4: String): Target = Target("value", listOf(1, 2), NestedTarget("foo"), key4)
+
+    @Test
+    fun testAllBlocks() = parametrizedTest { mode ->
+        val input = """{ /*beginning*/
+            /*before key*/ "key" /*after key*/ : /*after colon*/ "value" /*before comma*/,
+            "key2": [ /*array1*/ 1, /*array2*/ 2, /*end array*/],
+            "key3": { /*nested obj*/ "nestedKey": "foo"} /*after nested*/,
+            "key4": "/*comment inside quotes is a part of value*/",
+            /*before end*/
+        }"""
+        assertEquals(target("/*comment inside quotes is a part of value*/"), json.decodeFromString(input, mode))
+    }
+
+    @Test
+    fun testAllLines() = parametrizedTest { mode ->
+        val input = """{ //beginning
+            //before key
+            "key" // after key
+             : // after colon
+              "value" //before comma
+              ,
+            "key2": [ //array1
+             1, //array2
+              2, //end array
+              ],
+            "key3": { //nested obj
+            "nestedKey": "foo"
+            } , //after nested
+            "key4": "//comment inside quotes is a part of value",
+            //before end
+        }"""
+        assertEquals(target("//comment inside quotes is a part of value"), json.decodeFromString(input, mode))
+    }
+
+    @Test
+    fun testMixed() = parametrizedTest { mode ->
+        val input = """{ // begin
+           "key": "value", // after
+            "key2": /* array */ /*another comment */ [1, 2],
+            "key3": /* //this is a block comment */ { "nestedKey": // /*this is a line comment*/ "bar"
+                "foo" },
+            "key4": /* nesting block comments /* not supported */ "*/"
+        /* end */}"""
+        assertEquals(target("*/"), json.decodeFromString(input, mode))
+    }
+
+    @Test
+    fun testWeirdKeys() {
+        val map = mapOf(
+            "// comment inside quotes is a part of key" to "/* comment inside quotes is a part of value */",
+            "/*key */" to "/* value",
+            "/* key" to "*/ value"
+        )
+        val input = """/* before begin */
+            {
+            ${map.entries.joinToString(separator = ",\n") { (k, v) -> "\"$k\" : \"$v\"" }}
+            } // after end
+        """.trimIndent()
+        val afterMap = json.parseToJsonElement(input).jsonObject.mapValues { (_, v) ->
+            v as JsonPrimitive
+            assertTrue(v.isString)
+            v.content
+        }
+        assertEquals(map, afterMap)
+    }
+
+    @Test
+    fun testWithLenient() = parametrizedTest { mode ->
+        val input = """{ //beginning
+            //before key
+            key // after key
+             : // after colon
+              value //before comma
+              ,
+            key2: [ //array1
+             1, //array2
+              2, //end array
+              ],
+            key3: { //nested obj
+            nestedKey: "foo"
+            } , //after nested
+            key4: value//comment_cannot_break_value_apart, 
+            key5: //comment without quotes where new token expected is still a comment
+            value5,
+            //before end
+        }"""
+        assertEquals(target("value//comment_cannot_break_value_apart"), withLenient.decodeFromString(input, mode))
+    }
+
+    @Test
+    fun testUnclosedCommentsErrorMsg() = parametrizedTest { mode ->
+        val input = """{"data": "x"} // no newline"""
+        assertEquals(StringData("x"),  json.decodeFromString<StringData>(input, mode))
+        val input2 = """{"data": "x"} /* no endblock"""
+        assertFailsWith<SerializationException>("Expected end of the block comment: \"*/\", but had EOF instead at path: \$") {
+            json.decodeFromString<StringData>(input2, mode)
+        }
+    }
+
+    private val lexerBatchSize = 16 * 1024
+
+    @Test
+    fun testVeryLargeComments() = parametrizedTest { mode ->
+        val strLen = lexerBatchSize * 2 + 42
+        val inputLine = """{"data":  //a""" + "a".repeat(strLen) + "\n\"x\"}"
+        assertEquals(StringData("x"),  json.decodeFromString<StringData>(inputLine, mode))
+        val inputBlock = """{"data":  /*a""" + "a".repeat(strLen) + "*/\"x\"}"
+        assertEquals(StringData("x"),  json.decodeFromString<StringData>(inputBlock, mode))
+    }
+
+    @Test
+    fun testCommentsOnThresholdEdge() = parametrizedTest { mode ->
+        val inputPrefix = """{"data":  /*a"""
+        // Here, we test the situation when closing */ is divided in buffer:
+        // * fits in the initial buffer, but / is not.
+        // E.g. situation with batches looks like this: ['{', '"', 'd', ..., '*'], ['/', ...]
+        val bloatSize = lexerBatchSize - inputPrefix.length - 1
+        val inputLine = inputPrefix + "a".repeat(bloatSize) + "*/\"x\"}"
+        assertEquals(StringData("x"),  json.decodeFromString<StringData>(inputLine, mode))
+
+        // Test when * is unclosed and last in buffer:
+        val inputLine2 = inputPrefix + "a".repeat(bloatSize) + "*"
+        assertFailsWith<SerializationException>("Expected end of the block comment: \"*/\", but had EOF instead at path: \$") {
+            json.decodeFromString<StringData>(inputLine2, mode)
+        }
+
+    }
+
+}
diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/PolymorphismForCustomTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/PolymorphismForCustomTest.kt
new file mode 100644
index 0000000..1af959d
--- /dev/null
+++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/PolymorphismForCustomTest.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.features
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.modules.*
+import kotlin.test.*
+
+class PolymorphismForCustomTest : JsonTestBase() {
+
+    private val customSerializer = object : KSerializer<VImpl> {
+        override val descriptor: SerialDescriptor =
+            buildClassSerialDescriptor("VImpl") {
+                element("a", String.serializer().descriptor)
+            }
+
+        override fun deserialize(decoder: Decoder): VImpl {
+            decoder as JsonDecoder
+            val jsonObject = decoder.decodeJsonElement() as JsonObject
+            return VImpl(
+                (jsonObject["a"] as JsonPrimitive).content
+            )
+        }
+
+        override fun serialize(encoder: Encoder, value: VImpl) {
+            encoder as JsonEncoder
+            encoder.encodeJsonElement(
+                JsonObject(mapOf("a" to JsonPrimitive(value.a)))
+            )
+        }
+    }
+
+    @Serializable
+    data class ValueHolder<V : Any>(
+        @Polymorphic val value: V,
+    )
+
+    data class VImpl(val a: String)
+
+    val json = Json {
+        serializersModule = SerializersModule {
+            polymorphic(Any::class, VImpl::class, customSerializer)
+        }
+    }
+
+    @Test
+    fun test() = parametrizedTest { mode ->
+        val valueHolder = ValueHolder(VImpl("aaa"))
+        val encoded = json.encodeToString(ValueHolder.serializer(customSerializer), valueHolder, mode)
+        assertEquals("""{"value":{"type":"VImpl","a":"aaa"}}""", encoded)
+
+        val decoded = json.decodeFromString<ValueHolder<*>>(ValueHolder.serializer(customSerializer), encoded, mode)
+
+        assertEquals(valueHolder, decoded)
+    }
+
+}
diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/UseSerializersTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/UseSerializersTest.kt
index b5f332d..03cb9e7 100644
--- a/formats/json-tests/commonTest/src/kotlinx/serialization/features/UseSerializersTest.kt
+++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/UseSerializersTest.kt
@@ -21,9 +21,11 @@
 )
 
 class UseSerializersTest {
+    private val jsonWithDefaults = Json { encodeDefaults = true }
+
     @Test
     fun testOnFile() {
-        val str = Json { encodeDefaults = true }.encodeToString(
+        val str = jsonWithDefaults.encodeToString(
             Carrier2.serializer(),
             Carrier2(IntHolder(42), 2, 2, IntHolder(42))
         )
diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/UuidTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/UuidTest.kt
new file mode 100644
index 0000000..52f3b13
--- /dev/null
+++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/UuidTest.kt
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.features
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.modules.*
+import kotlin.test.*
+import kotlin.uuid.*
+
+class UuidTest : JsonTestBase() {
+    @Test
+    fun testPlainUuid() {
+        val uuid = Uuid.random()
+        assertJsonFormAndRestored(Uuid.serializer(), uuid, "\"$uuid\"")
+    }
+
+    // TODO: write a test without @Contextual after 2.1.0 release
+    @Serializable
+    data class Holder(@Contextual val uuid: Uuid)
+
+    val json = Json { serializersModule = serializersModuleOf(Uuid.serializer()) }
+
+    @Test
+    fun testNested() {
+        val fixed = Uuid.parse("bc501c76-d806-4578-b45e-97a264e280f1")
+        assertJsonFormAndRestored(
+            Holder.serializer(),
+            Holder(fixed),
+            """{"uuid":"bc501c76-d806-4578-b45e-97a264e280f1"}""",
+            json
+        )
+    }
+}
diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/features/sealed/SealedInterfacesInlineSerialNameTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/features/sealed/SealedInterfacesInlineSerialNameTest.kt
new file mode 100644
index 0000000..c86a5d3
--- /dev/null
+++ b/formats/json-tests/commonTest/src/kotlinx/serialization/features/sealed/SealedInterfacesInlineSerialNameTest.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.features.sealed
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlin.jvm.*
+import kotlin.test.*
+
+class SealedInterfacesInlineSerialNameTest : JsonTestBase() {
+    @Serializable
+    data class Child1Value(
+        val a: Int,
+        val b: String
+    )
+
+    @Serializable
+    data class Child2Value(
+        val c: Int,
+        val d: String
+    )
+
+    @Serializable
+    sealed interface Parent
+
+    @Serializable
+    @SerialName("child1")
+    @JvmInline
+    value class Child1(val value: Child1Value) : Parent
+
+    @Serializable
+    @SerialName("child2")
+    @JvmInline
+    value class Child2(val value: Child2Value) : Parent
+
+    // From https://github.com/Kotlin/kotlinx.serialization/issues/2288
+    @Test
+    fun testSealedInterfaceInlineSerialName() {
+        val messages = listOf(
+            Child1(Child1Value(1, "one")),
+            Child2(Child2Value(2, "two"))
+        )
+        assertJsonFormAndRestored(
+            serializer(),
+            messages,
+            """[{"type":"child1","a":1,"b":"one"},{"type":"child2","c":2,"d":"two"}]"""
+        )
+    }
+}
diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/AbstractJsonImplicitNullsTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/AbstractJsonImplicitNullsTest.kt
index a2f4a9d..7b5b327 100644
--- a/formats/json-tests/commonTest/src/kotlinx/serialization/json/AbstractJsonImplicitNullsTest.kt
+++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/AbstractJsonImplicitNullsTest.kt
@@ -84,8 +84,13 @@
     }
 
     @Test
-    fun testDecodeOptional() {
+    fun testOptional() {
+        val encoded = format.encode(WithOptional(null), WithOptional.serializer())
         val json = """{}"""
+        assertEquals(json, encoded)
+        // Same result when `null` is used instead of `1`:
+        val encodedWithNullInsteadOfDefault = format.encode(WithOptional(null, null), WithOptional.serializer())
+        assertEquals(json, encodedWithNullInsteadOfDefault)
 
         val decoded = format.decode(json, WithOptional.serializer())
         assertEquals(WithOptional(null), decoded)
diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonCoerceInputValuesTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonCoerceInputValuesTest.kt
index 3d7c332..5653d35 100644
--- a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonCoerceInputValuesTest.kt
+++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonCoerceInputValuesTest.kt
@@ -39,11 +39,19 @@
         val e: SampleEnum
     )
 
+    @Serializable
+    data class NullableEnumWithDefault(
+        val e: SampleEnum? = SampleEnum.OptionC
+    )
+
     val json = Json {
         coerceInputValues = true
         isLenient = true
     }
 
+    private val jsonNonLenient = Json(json) { isLenient = false }
+
+
     private fun <T> doTest(inputs: List<String>, expected: T, serializer: KSerializer<T>) {
         for (input in inputs) {
             parametrizedTest(json) {
@@ -79,7 +87,7 @@
             json.decodeFromString(WithEnum.serializer(), """{"e":{"x":"definitely not a valid enum value"}}""")
         }
         assertFailsWithSerial("JsonDecodingException") { // test user still sees exception on missing quotes
-            Json(json) { isLenient = false }.decodeFromString(WithEnum.serializer(), """{"e":unknown_value}""")
+            jsonNonLenient.decodeFromString(WithEnum.serializer(), """{"e":unknown_value}""")
         }
     }
 
@@ -142,4 +150,32 @@
             assertEquals(e2.message, e1.message)
         }
     }
+
+    @Test
+    fun testNullableEnumWithoutDefault() {
+        val j = Json(json) { explicitNulls = false }
+        parametrizedTest { mode ->
+            assertEquals(NullableEnumHolder(null), j.decodeFromString("{}"))
+            assertEquals(NullableEnumHolder(null), j.decodeFromString("""{"enum":"incorrect"}"""))
+        }
+    }
+
+    @Test
+    fun testNullableEnumWithoutDefaultDoesNotCoerceExplicitly() {
+        val j = Json(json) { explicitNulls = true }
+        parametrizedTest { mode ->
+            assertFailsWith<SerializationException> { j.decodeFromString<NullableEnumHolder>("{}") }
+            assertFailsWith<SerializationException> { j.decodeFromString<NullableEnumHolder>("""{"enum":"incorrect"}""") }
+        }
+    }
+
+    @Test
+    fun testNullableEnumWithDefault() {
+        val j = Json(json) { explicitNulls = false }
+        parametrizedTest { mode ->
+            assertEquals(NullableEnumWithDefault(), j.decodeFromString("{}"))
+            assertEquals(NullableEnumWithDefault(), j.decodeFromString("""{"e":"incorrect"}"""))
+            assertEquals(NullableEnumWithDefault(null), j.decodeFromString("""{"e":null}"""))
+        }
+    }
 }
diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonEncoderDecoderRecursiveTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonEncoderDecoderRecursiveTest.kt
index b4f7c71..b8d8e37 100644
--- a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonEncoderDecoderRecursiveTest.kt
+++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonEncoderDecoderRecursiveTest.kt
@@ -47,10 +47,12 @@
         assertEquals(inputDataString, ev)
     }
 
+    private val jsonPretty = Json { prettyPrint = true }
+
     @Test
     fun testWriteDataStringIndented() = parametrizedTest { jsonTestingMode ->
         val outputData = Event(0, Either.Right(Payload(42, 43, "Hello world")), 1000)
-        val ev = Json { prettyPrint = true }.encodeToString(Event.serializer(), outputData, jsonTestingMode)
+        val ev = jsonPretty.encodeToString(Event.serializer(), outputData, jsonTestingMode)
         assertEquals("""{
             |    "id": 0,
             |    "payload": {
diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonErrorMessagesTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonErrorMessagesTest.kt
index 08d1eef..da73bc3 100644
--- a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonErrorMessagesTest.kt
+++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonErrorMessagesTest.kt
@@ -6,12 +6,80 @@
 package kotlinx.serialization.json
 
 import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
 import kotlinx.serialization.test.*
 import kotlin.test.*
 
 
 class JsonErrorMessagesTest : JsonTestBase() {
 
+    @Serializable
+    @SerialName("app.Failure")
+    sealed interface Failure {
+        @Serializable
+        @SerialName("a")
+        data class A(val failure: Failure) : Failure
+    }
+
+    @Test
+    fun testPolymorphicCastMessage() = parametrizedTest { mode ->
+        checkSerializationException({
+            default.decodeFromString(
+                Failure.serializer(),
+                """{"type":"a", "failure":"wrong-input"}""",
+                mode
+            )
+        }, {
+            assertContains(
+                it,
+                "Expected JsonObject, but had JsonLiteral as the serialized body of app.Failure at element: \$.failure"
+            )
+        })
+    }
+
+    @Test
+    fun testPrimitiveInsteadOfObjectOrList() = parametrizedTest { mode ->
+        val input = """{"boxed": 42}"""
+        checkSerializationException({
+            default.decodeFromString(Box.serializer(StringData.serializer()), input, mode)
+        }, { message ->
+            if (mode == JsonTestingMode.TREE)
+                assertContains(message, "Expected JsonObject, but had JsonLiteral as the serialized body of kotlinx.serialization.StringData at element: \$.boxed")
+            else
+                assertContains(message, "Unexpected JSON token at offset 10: Expected start of the object '{', but had '4' instead at path: \$.boxed")
+        })
+
+        checkSerializationException({
+            default.decodeFromString(Box.serializer(ListSerializer(StringData.serializer())), input, mode)
+        }, { message ->
+            if (mode == JsonTestingMode.TREE)
+                assertContains(message, "Expected JsonArray, but had JsonLiteral as the serialized body of kotlin.collections.ArrayList at element: \$.boxed")
+            else
+                assertContains(message, "Unexpected JSON token at offset 10: Expected start of the array '[', but had '4' instead at path: \$.boxed")
+        })
+    }
+
+    @Test
+    fun testObjectOrListInsteadOfPrimitive() = parametrizedTest { mode ->
+        checkSerializationException({
+            default.decodeFromString(Box.serializer(Int.serializer()), """{"boxed": [1,2]}""", mode)
+        }, { message ->
+            if (mode == JsonTestingMode.TREE)
+                assertContains(message, "Expected JsonPrimitive, but had JsonArray as the serialized body of int at element: \$.boxed")
+            else
+                assertContains(message, "Unexpected JSON token at offset 10: Expected numeric literal at path: \$.boxed")
+        })
+
+        checkSerializationException({
+            default.decodeFromString(Box.serializer(String.serializer()), """{"boxed": {"x":"y"}}""", mode)
+        }, { message ->
+            if (mode == JsonTestingMode.TREE)
+                assertContains(message, "Expected JsonPrimitive, but had JsonObject as the serialized body of string at element: \$.boxed")
+            else
+                assertContains(message, "Unexpected JSON token at offset 10: Expected beginning of the string, but got { at path: \$.boxed")
+        })
+    }
+
     @Test
     fun testJsonTokensAreProperlyReported() = parametrizedTest { mode ->
         val input1 = """{"boxed":4}"""
@@ -24,7 +92,7 @@
             default.decodeFromString(serString, input1, mode)
         }, { message ->
             if (mode == JsonTestingMode.TREE)
-                assertContains(message, "String literal for key 'boxed' should be quoted.")
+                assertContains(message, "String literal for key 'boxed' should be quoted at element: \$.boxed")
             else
                 assertContains(
                     message,
@@ -42,7 +110,7 @@
                     "Unexpected JSON token at offset 9: Unexpected symbol 's' in numeric literal at path: \$.boxed"
                 )
             else
-                assertContains(message, "Failed to parse literal as 'int' value")
+                assertContains(message, "Failed to parse literal '\"str\"' as an int value at element: \$.boxed")
         })
     }
 
@@ -116,7 +184,7 @@
         }, { message ->
             if (mode == JsonTestingMode.TREE) assertContains(
                 message,
-                """String literal for key 'boxed' should be quoted."""
+                "String literal for key 'boxed' should be quoted at element: ${'$'}.boxed"
             )
             else assertContains(
                 message,
@@ -133,7 +201,7 @@
             default.decodeFromString(ser, input, mode)
         }, { message ->
             if (mode == JsonTestingMode.TREE)
-                assertContains(message, "Unexpected 'null' literal when non-nullable string was expected")
+                assertContains(message, "Expected string value for a non-null key 'boxed', got null literal instead at element: \$.boxed")
             else
                 assertContains(
                     message,
@@ -143,6 +211,23 @@
     }
 
     @Test
+    fun testNullLiteralForNotNullNumber() = parametrizedTest { mode ->
+        val input = """{"boxed":null}"""
+        val ser = serializer<Box<Int>>()
+        checkSerializationException({
+            default.decodeFromString(ser, input, mode)
+        }, { message ->
+            if (mode == JsonTestingMode.TREE)
+                assertContains(message, "Failed to parse literal 'null' as an int value at element: \$.boxed")
+            else
+                assertContains(
+                    message,
+                    "Unexpected JSON token at offset 9: Unexpected symbol 'n' in numeric literal at path: \$.boxed"
+                )
+        })
+    }
+
+    @Test
     fun testEof() = parametrizedTest { mode ->
         val input = """{"boxed":"""
         checkSerializationException({
diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonTestBase.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonTestBase.kt
index 6f3b132..de8cfb3 100644
--- a/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonTestBase.kt
+++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/JsonTestBase.kt
@@ -4,8 +4,10 @@
 
 package kotlinx.serialization.json
 
+import kotlinx.io.*
 import kotlinx.serialization.*
 import kotlinx.serialization.json.internal.*
+import kotlinx.serialization.json.io.*
 import kotlinx.serialization.json.okio.decodeFromBufferedSource
 import kotlinx.serialization.json.okio.encodeToBufferedSink
 import kotlinx.serialization.modules.EmptySerializersModule
@@ -14,13 +16,16 @@
 import kotlin.test.assertEquals
 import okio.*
 import kotlin.test.assertTrue
+import kotlinx.io.Buffer as KotlinxIoBuffer
+import okio.Buffer as OkioBuffer
 
 
 enum class JsonTestingMode {
     STREAMING,
     TREE,
     OKIO_STREAMS,
-    JAVA_STREAMS;
+    JAVA_STREAMS,
+    KXIO_STREAMS;
 
     companion object {
         fun value(i: Int) = values()[i]
@@ -53,10 +58,15 @@
                 encodeToString(tree)
             }
             JsonTestingMode.OKIO_STREAMS -> {
-                val buffer = Buffer()
+                val buffer = OkioBuffer()
                 encodeToBufferedSink(serializer, value, buffer)
                 buffer.readUtf8()
             }
+            JsonTestingMode.KXIO_STREAMS -> {
+                val buffer = KotlinxIoBuffer()
+                encodeToSink(serializer, value, buffer)
+                buffer.readString()
+            }
         }
 
     internal inline fun <reified T : Any> Json.decodeFromString(source: String, jsonTestingMode: JsonTestingMode): T {
@@ -81,10 +91,15 @@
                 readJson(this, tree, deserializer)
             }
             JsonTestingMode.OKIO_STREAMS -> {
-                val buffer = Buffer()
+                val buffer = OkioBuffer()
                 buffer.writeUtf8(source)
                 decodeFromBufferedSource(deserializer, buffer)
             }
+            JsonTestingMode.KXIO_STREAMS -> {
+                val buffer = KotlinxIoBuffer()
+                buffer.writeString(source)
+                decodeFromSource(deserializer, buffer)
+            }
         }
 
     protected open fun parametrizedTest(test: (JsonTestingMode) -> Unit) {
@@ -92,6 +107,7 @@
             add(runCatching { test(JsonTestingMode.STREAMING) })
             add(runCatching { test(JsonTestingMode.TREE) })
             add(runCatching { test(JsonTestingMode.OKIO_STREAMS) })
+            add(runCatching { test(JsonTestingMode.KXIO_STREAMS) })
 
             if (isJvm()) {
                 add(runCatching { test(JsonTestingMode.JAVA_STREAMS) })
@@ -117,7 +133,8 @@
         val streamingResult = runCatching { SwitchableJson(json, JsonTestingMode.STREAMING).test() }
         val treeResult = runCatching { SwitchableJson(json, JsonTestingMode.TREE).test() }
         val okioResult = runCatching { SwitchableJson(json, JsonTestingMode.OKIO_STREAMS).test() }
-        processResults(listOf(streamingResult, treeResult, okioResult))
+        val kxioResult = runCatching { SwitchableJson(json, JsonTestingMode.KXIO_STREAMS).test() }
+        processResults(listOf(streamingResult, treeResult, okioResult, kxioResult))
     }
 
     protected fun processResults(results: List<Result<*>>) {
diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonPolymorphismExceptionTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonPolymorphismExceptionTest.kt
index b7d4f12..6512bd3 100644
--- a/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonPolymorphismExceptionTest.kt
+++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/polymorphic/JsonPolymorphismExceptionTest.kt
@@ -31,8 +31,9 @@
             }
         }
 
+        val json = Json { serializersModule = serialModule }
         assertFailsWithSerial("JsonDecodingException") {
-            Json { serializersModule = serialModule }.decodeFromString(Base.serializer(), """{"type":"derived","nested":null}""", jsonTestingMode)
+            json.decodeFromString(Base.serializer(), """{"type":"derived","nested":null}""", jsonTestingMode)
         }
     }
 
@@ -44,8 +45,9 @@
             }
         }
 
+        val json = Json { serializersModule = serialModule }
         assertFailsWithSerial("JsonDecodingException") {
-            Json { serializersModule = serialModule }.decodeFromString(Base.serializer(), """{"nested":{}}""", jsonTestingMode)
+            json.decodeFromString(Base.serializer(), """{"nested":{}}""", jsonTestingMode)
         }
     }
 }
diff --git a/formats/json-tests/commonTest/src/kotlinx/serialization/json/serializers/JsonTreeTest.kt b/formats/json-tests/commonTest/src/kotlinx/serialization/json/serializers/JsonTreeTest.kt
index dd4d51e..1122332 100644
--- a/formats/json-tests/commonTest/src/kotlinx/serialization/json/serializers/JsonTreeTest.kt
+++ b/formats/json-tests/commonTest/src/kotlinx/serialization/json/serializers/JsonTreeTest.kt
@@ -47,7 +47,7 @@
         assertTrue(elem.getValue("c") is JsonArray)
 
         val array = elem.getValue("c").jsonArray
-        assertEquals("foo", array.getOrNull(0)?.jsonPrimitive?.content)
+        assertEquals("foo", array.getOrNull(0)?.jsonPrimitive?.contentOrNull)
         assertEquals(100500, array.getOrNull(1)?.jsonPrimitive?.int)
 
         assertTrue(array[2] is JsonObject)
diff --git a/formats/json-tests/jvmTest/resources/spec_cases/listing.txt b/formats/json-tests/jvmTest/resources/spec_cases/listing.txt
index c2f347c..f25066d 100644
--- a/formats/json-tests/jvmTest/resources/spec_cases/listing.txt
+++ b/formats/json-tests/jvmTest/resources/spec_cases/listing.txt
@@ -228,4 +228,4 @@
 y_structure_string_empty.json
 y_structure_trailing_newline.json
 y_structure_true_in_array.json
-y_structure_whitespace_array.json
\ No newline at end of file
+y_structure_whitespace_array.json
diff --git a/formats/json-tests/jvmTest/resources/spec_cases/listing_comments.txt b/formats/json-tests/jvmTest/resources/spec_cases/listing_comments.txt
new file mode 100644
index 0000000..f52b773
--- /dev/null
+++ b/formats/json-tests/jvmTest/resources/spec_cases/listing_comments.txt
@@ -0,0 +1,4 @@
+// Non-spec inputs that we accept with allowComments = true setting:
+n_object_trailing_comment.json
+n_object_trailing_comment_slash_open.json
+n_structure_object_with_comment.json
diff --git a/formats/json-tests/jvmTest/src/kotlinx/serialization/JvmMissingFieldsExceptionTest.kt b/formats/json-tests/jvmTest/src/kotlinx/serialization/JvmMissingFieldsExceptionTest.kt
index a423bc8..05b99cc 100644
--- a/formats/json-tests/jvmTest/src/kotlinx/serialization/JvmMissingFieldsExceptionTest.kt
+++ b/formats/json-tests/jvmTest/src/kotlinx/serialization/JvmMissingFieldsExceptionTest.kt
@@ -98,11 +98,12 @@
                 subclass(ChildA::class)
             }
         }
+        val json = Json {
+            serializersModule = module
+        }
 
         assertFailsWithMessages(listOf("p2", "c3")) {
-            Json {
-                serializersModule = module
-            }.decodeFromString<PolymorphicWrapper>("""{"nested": {"type": "a", "p1": 1, "c1": 11}}""")
+            json.decodeFromString<PolymorphicWrapper>("""{"nested": {"type": "a", "p1": 1, "c1": 11}}""")
         }
     }
 
diff --git a/formats/json-tests/jvmTest/src/kotlinx/serialization/UuidPlatformClashTest.kt b/formats/json-tests/jvmTest/src/kotlinx/serialization/UuidPlatformClashTest.kt
new file mode 100644
index 0000000..7871fac
--- /dev/null
+++ b/formats/json-tests/jvmTest/src/kotlinx/serialization/UuidPlatformClashTest.kt
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization
+
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.modules.*
+import kotlin.test.*
+import kotlin.uuid.*
+import java.util.UUID as JUuid
+import kotlin.uuid.Uuid as KUuid
+
+@OptIn(ExperimentalUuidApi::class)
+class UuidPlatformClashTest : JsonTestBase() {
+    object JavaUuidSerializer : KSerializer<JUuid> {
+        override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Uuid", PrimitiveKind.STRING)
+
+        override fun serialize(encoder: Encoder, value: JUuid) {
+            encoder.encodeString(value.toString().uppercase())
+        }
+
+        override fun deserialize(decoder: Decoder): JUuid {
+            return JUuid.fromString(decoder.decodeString())
+        }
+    }
+
+
+    @Serializable
+    data class UuidPair(
+        @Contextual val jUuid: JUuid,
+        @Contextual val kUuid: KUuid,
+    )
+
+    @Test
+    fun testUuids() {
+        val module = SerializersModule {
+            contextual(JavaUuidSerializer)
+            contextual(KUuid.serializer())
+        }
+        val json = Json { serializersModule = module }
+        val pair = UuidPair(
+            JUuid.fromString("252660b8-9a2b-44d1-a804-9a23f881cec5"),
+            KUuid.parse("c8ad1526-4b7c-4b67-9f77-6d05e580ad71")
+        )
+        assertJsonFormAndRestored(
+            UuidPair.serializer(),
+            pair,
+            """{"jUuid":"252660B8-9A2B-44D1-A804-9A23F881CEC5","kUuid":"c8ad1526-4b7c-4b67-9f77-6d05e580ad71"}""",
+            json
+        )
+    }
+}
diff --git a/formats/json-tests/jvmTest/src/kotlinx/serialization/features/SerializerByTypeTest.kt b/formats/json-tests/jvmTest/src/kotlinx/serialization/features/SerializerByTypeTest.kt
index a600b9d..94a83fd 100644
--- a/formats/json-tests/jvmTest/src/kotlinx/serialization/features/SerializerByTypeTest.kt
+++ b/formats/json-tests/jvmTest/src/kotlinx/serialization/features/SerializerByTypeTest.kt
@@ -275,18 +275,4 @@
             serializer(typeTokenOf<Array<NonSerializable>>())
         }
     }
-
-    @OptIn(ExperimentalTime::class)
-    @Test
-    fun testSerializersAreIntrinsified() {
-        val direct = measureTime {
-            Json.encodeToString(IntData.serializer(), IntData(10))
-        }
-        val directMs = direct.inWholeMicroseconds
-        val indirect = measureTime {
-            Json.encodeToString(IntData(10))
-        }
-        val indirectMs = indirect.inWholeMicroseconds
-        if (indirectMs > directMs + (directMs / 4)) error("Direct ($directMs) and indirect ($indirectMs) times are too far apart")
-    }
 }
diff --git a/formats/json-tests/jvmTest/src/kotlinx/serialization/json/SpecConformanceTest.kt b/formats/json-tests/jvmTest/src/kotlinx/serialization/json/SpecConformanceTest.kt
index 96401f7..458eb04 100644
--- a/formats/json-tests/jvmTest/src/kotlinx/serialization/json/SpecConformanceTest.kt
+++ b/formats/json-tests/jvmTest/src/kotlinx/serialization/json/SpecConformanceTest.kt
Binary files differ
diff --git a/formats/json-tests/nativeTest/src/kotlinx/serialization/json/MultiWorkerJsonTest.kt b/formats/json-tests/nativeTest/src/kotlinx/serialization/json/MultiWorkerJsonTest.kt
index 2ea063d..8c1ea08 100644
--- a/formats/json-tests/nativeTest/src/kotlinx/serialization/json/MultiWorkerJsonTest.kt
+++ b/formats/json-tests/nativeTest/src/kotlinx/serialization/json/MultiWorkerJsonTest.kt
@@ -22,7 +22,7 @@
                 assertEquals(PlainOne(42), json().decodeFromString("""{"one":42,"two":239}"""))
             }
         }
-        worker.executeAfter(1000, operation.freeze())
+        worker.executeAfter(1000, operation)
         for (i in 0..999) {
             assertEquals(PlainTwo(239), json().decodeFromString("""{"one":42,"two":239}"""))
         }
diff --git a/formats/json/api/kotlinx-serialization-json.api b/formats/json/api/kotlinx-serialization-json.api
index 6874d74..4602ad3 100644
--- a/formats/json/api/kotlinx-serialization-json.api
+++ b/formats/json/api/kotlinx-serialization-json.api
@@ -94,6 +94,7 @@
 }
 
 public final class kotlinx/serialization/json/JsonBuilder {
+	public final fun getAllowComments ()Z
 	public final fun getAllowSpecialFloatingPointValues ()Z
 	public final fun getAllowStructuredMapKeys ()Z
 	public final fun getAllowTrailingComma ()Z
@@ -111,6 +112,7 @@
 	public final fun getUseAlternativeNames ()Z
 	public final fun getUseArrayPolymorphism ()Z
 	public final fun isLenient ()Z
+	public final fun setAllowComments (Z)V
 	public final fun setAllowSpecialFloatingPointValues (Z)V
 	public final fun setAllowStructuredMapKeys (Z)V
 	public final fun setAllowTrailingComma (Z)V
@@ -141,6 +143,7 @@
 
 public final class kotlinx/serialization/json/JsonConfiguration {
 	public fun <init> ()V
+	public final fun getAllowComments ()Z
 	public final fun getAllowSpecialFloatingPointValues ()Z
 	public final fun getAllowStructuredMapKeys ()Z
 	public final fun getAllowTrailingComma ()Z
@@ -404,7 +407,15 @@
 	public abstract fun read ([CII)I
 }
 
+public abstract class kotlinx/serialization/json/internal/InternalJsonReaderCodePointImpl : kotlinx/serialization/json/internal/InternalJsonReader {
+	public fun <init> ()V
+	public abstract fun exhausted ()Z
+	public abstract fun nextCodePoint ()I
+	public final fun read ([CII)I
+}
+
 public abstract interface class kotlinx/serialization/json/internal/InternalJsonWriter {
+	public static final field Companion Lkotlinx/serialization/json/internal/InternalJsonWriter$Companion;
 	public abstract fun release ()V
 	public abstract fun write (Ljava/lang/String;)V
 	public abstract fun writeChar (C)V
@@ -412,6 +423,10 @@
 	public abstract fun writeQuoted (Ljava/lang/String;)V
 }
 
+public final class kotlinx/serialization/json/internal/InternalJsonWriter$Companion {
+	public final fun doWriteEscaping (Ljava/lang/String;Lkotlin/jvm/functions/Function3;)V
+}
+
 public final class kotlinx/serialization/json/internal/JsonStreamsKt {
 	public static final fun decodeByReader (Lkotlinx/serialization/json/Json;Lkotlinx/serialization/DeserializationStrategy;Lkotlinx/serialization/json/internal/InternalJsonReader;)Ljava/lang/Object;
 	public static final fun decodeToSequenceByReader (Lkotlinx/serialization/json/Json;Lkotlinx/serialization/json/internal/InternalJsonReader;Lkotlinx/serialization/DeserializationStrategy;Lkotlinx/serialization/json/DecodeSequenceMode;)Lkotlin/sequences/Sequence;
@@ -423,6 +438,10 @@
 	public static final fun decodeStringToJsonTree (Lkotlinx/serialization/json/Json;Lkotlinx/serialization/DeserializationStrategy;Ljava/lang/String;)Lkotlinx/serialization/json/JsonElement;
 }
 
+public final class kotlinx/serialization/json/internal/StringOpsKt {
+	public static final fun getESCAPE_STRINGS ()[Ljava/lang/String;
+}
+
 public final class kotlinx/serialization/json/internal/TreeJsonDecoderKt {
 	public static final fun readJson (Lkotlinx/serialization/json/Json;Lkotlinx/serialization/json/JsonElement;Lkotlinx/serialization/DeserializationStrategy;)Ljava/lang/Object;
 }
diff --git a/formats/json/api/kotlinx-serialization-json.klib.api b/formats/json/api/kotlinx-serialization-json.klib.api
new file mode 100644
index 0000000..4262840
--- /dev/null
+++ b/formats/json/api/kotlinx-serialization-json.klib.api
@@ -0,0 +1,465 @@
+// Klib ABI Dump
+// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, js, linuxArm32Hfp, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, wasmJs, wasmWasi, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64]
+// Rendering settings:
+// - Signature version: 2
+// - Show manifest properties: true
+// - Show declarations: true
+
+// Library unique name: <org.jetbrains.kotlinx:kotlinx-serialization-json>
+open annotation class kotlinx.serialization.json.internal/FormatLanguage : kotlin/Annotation { // kotlinx.serialization.json.internal/FormatLanguage|null[0]
+    constructor <init>(kotlin/String, kotlin/String, kotlin/String) // kotlinx.serialization.json.internal/FormatLanguage.<init>|<init>(kotlin.String;kotlin.String;kotlin.String){}[0]
+
+    final val prefix // kotlinx.serialization.json.internal/FormatLanguage.prefix|{}prefix[0]
+        final fun <get-prefix>(): kotlin/String // kotlinx.serialization.json.internal/FormatLanguage.prefix.<get-prefix>|<get-prefix>(){}[0]
+    final val suffix // kotlinx.serialization.json.internal/FormatLanguage.suffix|{}suffix[0]
+        final fun <get-suffix>(): kotlin/String // kotlinx.serialization.json.internal/FormatLanguage.suffix.<get-suffix>|<get-suffix>(){}[0]
+    final val value // kotlinx.serialization.json.internal/FormatLanguage.value|{}value[0]
+        final fun <get-value>(): kotlin/String // kotlinx.serialization.json.internal/FormatLanguage.value.<get-value>|<get-value>(){}[0]
+}
+
+open annotation class kotlinx.serialization.json/JsonClassDiscriminator : kotlin/Annotation { // kotlinx.serialization.json/JsonClassDiscriminator|null[0]
+    constructor <init>(kotlin/String) // kotlinx.serialization.json/JsonClassDiscriminator.<init>|<init>(kotlin.String){}[0]
+
+    final val discriminator // kotlinx.serialization.json/JsonClassDiscriminator.discriminator|{}discriminator[0]
+        final fun <get-discriminator>(): kotlin/String // kotlinx.serialization.json/JsonClassDiscriminator.discriminator.<get-discriminator>|<get-discriminator>(){}[0]
+}
+
+open annotation class kotlinx.serialization.json/JsonNames : kotlin/Annotation { // kotlinx.serialization.json/JsonNames|null[0]
+    constructor <init>(kotlin/Array<out kotlin/String>...) // kotlinx.serialization.json/JsonNames.<init>|<init>(kotlin.Array<out|kotlin.String>...){}[0]
+
+    final val names // kotlinx.serialization.json/JsonNames.names|{}names[0]
+        final fun <get-names>(): kotlin/Array<out kotlin/String> // kotlinx.serialization.json/JsonNames.names.<get-names>|<get-names>(){}[0]
+}
+
+final enum class kotlinx.serialization.json/ClassDiscriminatorMode : kotlin/Enum<kotlinx.serialization.json/ClassDiscriminatorMode> { // kotlinx.serialization.json/ClassDiscriminatorMode|null[0]
+    enum entry ALL_JSON_OBJECTS // kotlinx.serialization.json/ClassDiscriminatorMode.ALL_JSON_OBJECTS|null[0]
+    enum entry NONE // kotlinx.serialization.json/ClassDiscriminatorMode.NONE|null[0]
+    enum entry POLYMORPHIC // kotlinx.serialization.json/ClassDiscriminatorMode.POLYMORPHIC|null[0]
+
+    final val entries // kotlinx.serialization.json/ClassDiscriminatorMode.entries|#static{}entries[0]
+        final fun <get-entries>(): kotlin.enums/EnumEntries<kotlinx.serialization.json/ClassDiscriminatorMode> // kotlinx.serialization.json/ClassDiscriminatorMode.entries.<get-entries>|<get-entries>#static(){}[0]
+
+    final fun valueOf(kotlin/String): kotlinx.serialization.json/ClassDiscriminatorMode // kotlinx.serialization.json/ClassDiscriminatorMode.valueOf|valueOf#static(kotlin.String){}[0]
+    final fun values(): kotlin/Array<kotlinx.serialization.json/ClassDiscriminatorMode> // kotlinx.serialization.json/ClassDiscriminatorMode.values|values#static(){}[0]
+}
+
+final enum class kotlinx.serialization.json/DecodeSequenceMode : kotlin/Enum<kotlinx.serialization.json/DecodeSequenceMode> { // kotlinx.serialization.json/DecodeSequenceMode|null[0]
+    enum entry ARRAY_WRAPPED // kotlinx.serialization.json/DecodeSequenceMode.ARRAY_WRAPPED|null[0]
+    enum entry AUTO_DETECT // kotlinx.serialization.json/DecodeSequenceMode.AUTO_DETECT|null[0]
+    enum entry WHITESPACE_SEPARATED // kotlinx.serialization.json/DecodeSequenceMode.WHITESPACE_SEPARATED|null[0]
+
+    final val entries // kotlinx.serialization.json/DecodeSequenceMode.entries|#static{}entries[0]
+        final fun <get-entries>(): kotlin.enums/EnumEntries<kotlinx.serialization.json/DecodeSequenceMode> // kotlinx.serialization.json/DecodeSequenceMode.entries.<get-entries>|<get-entries>#static(){}[0]
+
+    final fun valueOf(kotlin/String): kotlinx.serialization.json/DecodeSequenceMode // kotlinx.serialization.json/DecodeSequenceMode.valueOf|valueOf#static(kotlin.String){}[0]
+    final fun values(): kotlin/Array<kotlinx.serialization.json/DecodeSequenceMode> // kotlinx.serialization.json/DecodeSequenceMode.values|values#static(){}[0]
+}
+
+abstract fun interface kotlinx.serialization.json/JsonNamingStrategy { // kotlinx.serialization.json/JsonNamingStrategy|null[0]
+    abstract fun serialNameForJson(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/Int, kotlin/String): kotlin/String // kotlinx.serialization.json/JsonNamingStrategy.serialNameForJson|serialNameForJson(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.Int;kotlin.String){}[0]
+
+    final object Builtins { // kotlinx.serialization.json/JsonNamingStrategy.Builtins|null[0]
+        final val KebabCase // kotlinx.serialization.json/JsonNamingStrategy.Builtins.KebabCase|{}KebabCase[0]
+            final fun <get-KebabCase>(): kotlinx.serialization.json/JsonNamingStrategy // kotlinx.serialization.json/JsonNamingStrategy.Builtins.KebabCase.<get-KebabCase>|<get-KebabCase>(){}[0]
+        final val SnakeCase // kotlinx.serialization.json/JsonNamingStrategy.Builtins.SnakeCase|{}SnakeCase[0]
+            final fun <get-SnakeCase>(): kotlinx.serialization.json/JsonNamingStrategy // kotlinx.serialization.json/JsonNamingStrategy.Builtins.SnakeCase.<get-SnakeCase>|<get-SnakeCase>(){}[0]
+    }
+}
+
+abstract interface kotlinx.serialization.json.internal/InternalJsonReader { // kotlinx.serialization.json.internal/InternalJsonReader|null[0]
+    abstract fun read(kotlin/CharArray, kotlin/Int, kotlin/Int): kotlin/Int // kotlinx.serialization.json.internal/InternalJsonReader.read|read(kotlin.CharArray;kotlin.Int;kotlin.Int){}[0]
+}
+
+abstract interface kotlinx.serialization.json.internal/InternalJsonWriter { // kotlinx.serialization.json.internal/InternalJsonWriter|null[0]
+    abstract fun release() // kotlinx.serialization.json.internal/InternalJsonWriter.release|release(){}[0]
+    abstract fun write(kotlin/String) // kotlinx.serialization.json.internal/InternalJsonWriter.write|write(kotlin.String){}[0]
+    abstract fun writeChar(kotlin/Char) // kotlinx.serialization.json.internal/InternalJsonWriter.writeChar|writeChar(kotlin.Char){}[0]
+    abstract fun writeLong(kotlin/Long) // kotlinx.serialization.json.internal/InternalJsonWriter.writeLong|writeLong(kotlin.Long){}[0]
+    abstract fun writeQuoted(kotlin/String) // kotlinx.serialization.json.internal/InternalJsonWriter.writeQuoted|writeQuoted(kotlin.String){}[0]
+
+    final object Companion { // kotlinx.serialization.json.internal/InternalJsonWriter.Companion|null[0]
+        final inline fun doWriteEscaping(kotlin/String, kotlin/Function3<kotlin/String, kotlin/Int, kotlin/Int, kotlin/Unit>) // kotlinx.serialization.json.internal/InternalJsonWriter.Companion.doWriteEscaping|doWriteEscaping(kotlin.String;kotlin.Function3<kotlin.String,kotlin.Int,kotlin.Int,kotlin.Unit>){}[0]
+    }
+}
+
+abstract interface kotlinx.serialization.json/JsonDecoder : kotlinx.serialization.encoding/CompositeDecoder, kotlinx.serialization.encoding/Decoder { // kotlinx.serialization.json/JsonDecoder|null[0]
+    abstract val json // kotlinx.serialization.json/JsonDecoder.json|{}json[0]
+        abstract fun <get-json>(): kotlinx.serialization.json/Json // kotlinx.serialization.json/JsonDecoder.json.<get-json>|<get-json>(){}[0]
+
+    abstract fun decodeJsonElement(): kotlinx.serialization.json/JsonElement // kotlinx.serialization.json/JsonDecoder.decodeJsonElement|decodeJsonElement(){}[0]
+}
+
+abstract interface kotlinx.serialization.json/JsonEncoder : kotlinx.serialization.encoding/CompositeEncoder, kotlinx.serialization.encoding/Encoder { // kotlinx.serialization.json/JsonEncoder|null[0]
+    abstract val json // kotlinx.serialization.json/JsonEncoder.json|{}json[0]
+        abstract fun <get-json>(): kotlinx.serialization.json/Json // kotlinx.serialization.json/JsonEncoder.json.<get-json>|<get-json>(){}[0]
+
+    abstract fun encodeJsonElement(kotlinx.serialization.json/JsonElement) // kotlinx.serialization.json/JsonEncoder.encodeJsonElement|encodeJsonElement(kotlinx.serialization.json.JsonElement){}[0]
+}
+
+abstract class <#A: kotlin/Any> kotlinx.serialization.json/JsonContentPolymorphicSerializer : kotlinx.serialization/KSerializer<#A> { // kotlinx.serialization.json/JsonContentPolymorphicSerializer|null[0]
+    constructor <init>(kotlin.reflect/KClass<#A>) // kotlinx.serialization.json/JsonContentPolymorphicSerializer.<init>|<init>(kotlin.reflect.KClass<1:0>){}[0]
+
+    open val descriptor // kotlinx.serialization.json/JsonContentPolymorphicSerializer.descriptor|{}descriptor[0]
+        open fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.json/JsonContentPolymorphicSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+
+    abstract fun selectDeserializer(kotlinx.serialization.json/JsonElement): kotlinx.serialization/DeserializationStrategy<#A> // kotlinx.serialization.json/JsonContentPolymorphicSerializer.selectDeserializer|selectDeserializer(kotlinx.serialization.json.JsonElement){}[0]
+    final fun deserialize(kotlinx.serialization.encoding/Decoder): #A // kotlinx.serialization.json/JsonContentPolymorphicSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
+    final fun serialize(kotlinx.serialization.encoding/Encoder, #A) // kotlinx.serialization.json/JsonContentPolymorphicSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;1:0){}[0]
+}
+
+abstract class <#A: kotlin/Any> kotlinx.serialization.json/JsonTransformingSerializer : kotlinx.serialization/KSerializer<#A> { // kotlinx.serialization.json/JsonTransformingSerializer|null[0]
+    constructor <init>(kotlinx.serialization/KSerializer<#A>) // kotlinx.serialization.json/JsonTransformingSerializer.<init>|<init>(kotlinx.serialization.KSerializer<1:0>){}[0]
+
+    open val descriptor // kotlinx.serialization.json/JsonTransformingSerializer.descriptor|{}descriptor[0]
+        open fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.json/JsonTransformingSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+
+    final fun deserialize(kotlinx.serialization.encoding/Decoder): #A // kotlinx.serialization.json/JsonTransformingSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
+    final fun serialize(kotlinx.serialization.encoding/Encoder, #A) // kotlinx.serialization.json/JsonTransformingSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;1:0){}[0]
+    open fun transformDeserialize(kotlinx.serialization.json/JsonElement): kotlinx.serialization.json/JsonElement // kotlinx.serialization.json/JsonTransformingSerializer.transformDeserialize|transformDeserialize(kotlinx.serialization.json.JsonElement){}[0]
+    open fun transformSerialize(kotlinx.serialization.json/JsonElement): kotlinx.serialization.json/JsonElement // kotlinx.serialization.json/JsonTransformingSerializer.transformSerialize|transformSerialize(kotlinx.serialization.json.JsonElement){}[0]
+}
+
+abstract class kotlinx.serialization.json.internal/InternalJsonReaderCodePointImpl : kotlinx.serialization.json.internal/InternalJsonReader { // kotlinx.serialization.json.internal/InternalJsonReaderCodePointImpl|null[0]
+    constructor <init>() // kotlinx.serialization.json.internal/InternalJsonReaderCodePointImpl.<init>|<init>(){}[0]
+
+    abstract fun exhausted(): kotlin/Boolean // kotlinx.serialization.json.internal/InternalJsonReaderCodePointImpl.exhausted|exhausted(){}[0]
+    abstract fun nextCodePoint(): kotlin/Int // kotlinx.serialization.json.internal/InternalJsonReaderCodePointImpl.nextCodePoint|nextCodePoint(){}[0]
+    final fun read(kotlin/CharArray, kotlin/Int, kotlin/Int): kotlin/Int // kotlinx.serialization.json.internal/InternalJsonReaderCodePointImpl.read|read(kotlin.CharArray;kotlin.Int;kotlin.Int){}[0]
+}
+
+final class kotlinx.serialization.json/JsonArray : kotlin.collections/List<kotlinx.serialization.json/JsonElement>, kotlinx.serialization.json/JsonElement { // kotlinx.serialization.json/JsonArray|null[0]
+    constructor <init>(kotlin.collections/List<kotlinx.serialization.json/JsonElement>) // kotlinx.serialization.json/JsonArray.<init>|<init>(kotlin.collections.List<kotlinx.serialization.json.JsonElement>){}[0]
+
+    final val size // kotlinx.serialization.json/JsonArray.size|{}size[0]
+        final fun <get-size>(): kotlin/Int // kotlinx.serialization.json/JsonArray.size.<get-size>|<get-size>(){}[0]
+
+    final fun contains(kotlinx.serialization.json/JsonElement): kotlin/Boolean // kotlinx.serialization.json/JsonArray.contains|contains(kotlinx.serialization.json.JsonElement){}[0]
+    final fun containsAll(kotlin.collections/Collection<kotlinx.serialization.json/JsonElement>): kotlin/Boolean // kotlinx.serialization.json/JsonArray.containsAll|containsAll(kotlin.collections.Collection<kotlinx.serialization.json.JsonElement>){}[0]
+    final fun equals(kotlin/Any?): kotlin/Boolean // kotlinx.serialization.json/JsonArray.equals|equals(kotlin.Any?){}[0]
+    final fun get(kotlin/Int): kotlinx.serialization.json/JsonElement // kotlinx.serialization.json/JsonArray.get|get(kotlin.Int){}[0]
+    final fun hashCode(): kotlin/Int // kotlinx.serialization.json/JsonArray.hashCode|hashCode(){}[0]
+    final fun indexOf(kotlinx.serialization.json/JsonElement): kotlin/Int // kotlinx.serialization.json/JsonArray.indexOf|indexOf(kotlinx.serialization.json.JsonElement){}[0]
+    final fun isEmpty(): kotlin/Boolean // kotlinx.serialization.json/JsonArray.isEmpty|isEmpty(){}[0]
+    final fun iterator(): kotlin.collections/Iterator<kotlinx.serialization.json/JsonElement> // kotlinx.serialization.json/JsonArray.iterator|iterator(){}[0]
+    final fun lastIndexOf(kotlinx.serialization.json/JsonElement): kotlin/Int // kotlinx.serialization.json/JsonArray.lastIndexOf|lastIndexOf(kotlinx.serialization.json.JsonElement){}[0]
+    final fun listIterator(): kotlin.collections/ListIterator<kotlinx.serialization.json/JsonElement> // kotlinx.serialization.json/JsonArray.listIterator|listIterator(){}[0]
+    final fun listIterator(kotlin/Int): kotlin.collections/ListIterator<kotlinx.serialization.json/JsonElement> // kotlinx.serialization.json/JsonArray.listIterator|listIterator(kotlin.Int){}[0]
+    final fun subList(kotlin/Int, kotlin/Int): kotlin.collections/List<kotlinx.serialization.json/JsonElement> // kotlinx.serialization.json/JsonArray.subList|subList(kotlin.Int;kotlin.Int){}[0]
+    final fun toString(): kotlin/String // kotlinx.serialization.json/JsonArray.toString|toString(){}[0]
+
+    final object Companion { // kotlinx.serialization.json/JsonArray.Companion|null[0]
+        final fun serializer(): kotlinx.serialization/KSerializer<kotlinx.serialization.json/JsonArray> // kotlinx.serialization.json/JsonArray.Companion.serializer|serializer(){}[0]
+    }
+
+    // Targets: [js]
+    final fun asJsReadonlyArrayView(): kotlin.js.collections/JsReadonlyArray<kotlinx.serialization.json/JsonElement> // kotlinx.serialization.json/JsonArray.asJsReadonlyArrayView|asJsReadonlyArrayView(){}[0]
+}
+
+final class kotlinx.serialization.json/JsonArrayBuilder { // kotlinx.serialization.json/JsonArrayBuilder|null[0]
+    constructor <init>() // kotlinx.serialization.json/JsonArrayBuilder.<init>|<init>(){}[0]
+
+    final fun add(kotlinx.serialization.json/JsonElement): kotlin/Boolean // kotlinx.serialization.json/JsonArrayBuilder.add|add(kotlinx.serialization.json.JsonElement){}[0]
+    final fun addAll(kotlin.collections/Collection<kotlinx.serialization.json/JsonElement>): kotlin/Boolean // kotlinx.serialization.json/JsonArrayBuilder.addAll|addAll(kotlin.collections.Collection<kotlinx.serialization.json.JsonElement>){}[0]
+    final fun build(): kotlinx.serialization.json/JsonArray // kotlinx.serialization.json/JsonArrayBuilder.build|build(){}[0]
+}
+
+final class kotlinx.serialization.json/JsonBuilder { // kotlinx.serialization.json/JsonBuilder|null[0]
+    final var allowComments // kotlinx.serialization.json/JsonBuilder.allowComments|{}allowComments[0]
+        final fun <get-allowComments>(): kotlin/Boolean // kotlinx.serialization.json/JsonBuilder.allowComments.<get-allowComments>|<get-allowComments>(){}[0]
+        final fun <set-allowComments>(kotlin/Boolean) // kotlinx.serialization.json/JsonBuilder.allowComments.<set-allowComments>|<set-allowComments>(kotlin.Boolean){}[0]
+    final var allowSpecialFloatingPointValues // kotlinx.serialization.json/JsonBuilder.allowSpecialFloatingPointValues|{}allowSpecialFloatingPointValues[0]
+        final fun <get-allowSpecialFloatingPointValues>(): kotlin/Boolean // kotlinx.serialization.json/JsonBuilder.allowSpecialFloatingPointValues.<get-allowSpecialFloatingPointValues>|<get-allowSpecialFloatingPointValues>(){}[0]
+        final fun <set-allowSpecialFloatingPointValues>(kotlin/Boolean) // kotlinx.serialization.json/JsonBuilder.allowSpecialFloatingPointValues.<set-allowSpecialFloatingPointValues>|<set-allowSpecialFloatingPointValues>(kotlin.Boolean){}[0]
+    final var allowStructuredMapKeys // kotlinx.serialization.json/JsonBuilder.allowStructuredMapKeys|{}allowStructuredMapKeys[0]
+        final fun <get-allowStructuredMapKeys>(): kotlin/Boolean // kotlinx.serialization.json/JsonBuilder.allowStructuredMapKeys.<get-allowStructuredMapKeys>|<get-allowStructuredMapKeys>(){}[0]
+        final fun <set-allowStructuredMapKeys>(kotlin/Boolean) // kotlinx.serialization.json/JsonBuilder.allowStructuredMapKeys.<set-allowStructuredMapKeys>|<set-allowStructuredMapKeys>(kotlin.Boolean){}[0]
+    final var allowTrailingComma // kotlinx.serialization.json/JsonBuilder.allowTrailingComma|{}allowTrailingComma[0]
+        final fun <get-allowTrailingComma>(): kotlin/Boolean // kotlinx.serialization.json/JsonBuilder.allowTrailingComma.<get-allowTrailingComma>|<get-allowTrailingComma>(){}[0]
+        final fun <set-allowTrailingComma>(kotlin/Boolean) // kotlinx.serialization.json/JsonBuilder.allowTrailingComma.<set-allowTrailingComma>|<set-allowTrailingComma>(kotlin.Boolean){}[0]
+    final var classDiscriminator // kotlinx.serialization.json/JsonBuilder.classDiscriminator|{}classDiscriminator[0]
+        final fun <get-classDiscriminator>(): kotlin/String // kotlinx.serialization.json/JsonBuilder.classDiscriminator.<get-classDiscriminator>|<get-classDiscriminator>(){}[0]
+        final fun <set-classDiscriminator>(kotlin/String) // kotlinx.serialization.json/JsonBuilder.classDiscriminator.<set-classDiscriminator>|<set-classDiscriminator>(kotlin.String){}[0]
+    final var classDiscriminatorMode // kotlinx.serialization.json/JsonBuilder.classDiscriminatorMode|{}classDiscriminatorMode[0]
+        final fun <get-classDiscriminatorMode>(): kotlinx.serialization.json/ClassDiscriminatorMode // kotlinx.serialization.json/JsonBuilder.classDiscriminatorMode.<get-classDiscriminatorMode>|<get-classDiscriminatorMode>(){}[0]
+        final fun <set-classDiscriminatorMode>(kotlinx.serialization.json/ClassDiscriminatorMode) // kotlinx.serialization.json/JsonBuilder.classDiscriminatorMode.<set-classDiscriminatorMode>|<set-classDiscriminatorMode>(kotlinx.serialization.json.ClassDiscriminatorMode){}[0]
+    final var coerceInputValues // kotlinx.serialization.json/JsonBuilder.coerceInputValues|{}coerceInputValues[0]
+        final fun <get-coerceInputValues>(): kotlin/Boolean // kotlinx.serialization.json/JsonBuilder.coerceInputValues.<get-coerceInputValues>|<get-coerceInputValues>(){}[0]
+        final fun <set-coerceInputValues>(kotlin/Boolean) // kotlinx.serialization.json/JsonBuilder.coerceInputValues.<set-coerceInputValues>|<set-coerceInputValues>(kotlin.Boolean){}[0]
+    final var decodeEnumsCaseInsensitive // kotlinx.serialization.json/JsonBuilder.decodeEnumsCaseInsensitive|{}decodeEnumsCaseInsensitive[0]
+        final fun <get-decodeEnumsCaseInsensitive>(): kotlin/Boolean // kotlinx.serialization.json/JsonBuilder.decodeEnumsCaseInsensitive.<get-decodeEnumsCaseInsensitive>|<get-decodeEnumsCaseInsensitive>(){}[0]
+        final fun <set-decodeEnumsCaseInsensitive>(kotlin/Boolean) // kotlinx.serialization.json/JsonBuilder.decodeEnumsCaseInsensitive.<set-decodeEnumsCaseInsensitive>|<set-decodeEnumsCaseInsensitive>(kotlin.Boolean){}[0]
+    final var encodeDefaults // kotlinx.serialization.json/JsonBuilder.encodeDefaults|{}encodeDefaults[0]
+        final fun <get-encodeDefaults>(): kotlin/Boolean // kotlinx.serialization.json/JsonBuilder.encodeDefaults.<get-encodeDefaults>|<get-encodeDefaults>(){}[0]
+        final fun <set-encodeDefaults>(kotlin/Boolean) // kotlinx.serialization.json/JsonBuilder.encodeDefaults.<set-encodeDefaults>|<set-encodeDefaults>(kotlin.Boolean){}[0]
+    final var explicitNulls // kotlinx.serialization.json/JsonBuilder.explicitNulls|{}explicitNulls[0]
+        final fun <get-explicitNulls>(): kotlin/Boolean // kotlinx.serialization.json/JsonBuilder.explicitNulls.<get-explicitNulls>|<get-explicitNulls>(){}[0]
+        final fun <set-explicitNulls>(kotlin/Boolean) // kotlinx.serialization.json/JsonBuilder.explicitNulls.<set-explicitNulls>|<set-explicitNulls>(kotlin.Boolean){}[0]
+    final var ignoreUnknownKeys // kotlinx.serialization.json/JsonBuilder.ignoreUnknownKeys|{}ignoreUnknownKeys[0]
+        final fun <get-ignoreUnknownKeys>(): kotlin/Boolean // kotlinx.serialization.json/JsonBuilder.ignoreUnknownKeys.<get-ignoreUnknownKeys>|<get-ignoreUnknownKeys>(){}[0]
+        final fun <set-ignoreUnknownKeys>(kotlin/Boolean) // kotlinx.serialization.json/JsonBuilder.ignoreUnknownKeys.<set-ignoreUnknownKeys>|<set-ignoreUnknownKeys>(kotlin.Boolean){}[0]
+    final var isLenient // kotlinx.serialization.json/JsonBuilder.isLenient|{}isLenient[0]
+        final fun <get-isLenient>(): kotlin/Boolean // kotlinx.serialization.json/JsonBuilder.isLenient.<get-isLenient>|<get-isLenient>(){}[0]
+        final fun <set-isLenient>(kotlin/Boolean) // kotlinx.serialization.json/JsonBuilder.isLenient.<set-isLenient>|<set-isLenient>(kotlin.Boolean){}[0]
+    final var namingStrategy // kotlinx.serialization.json/JsonBuilder.namingStrategy|{}namingStrategy[0]
+        final fun <get-namingStrategy>(): kotlinx.serialization.json/JsonNamingStrategy? // kotlinx.serialization.json/JsonBuilder.namingStrategy.<get-namingStrategy>|<get-namingStrategy>(){}[0]
+        final fun <set-namingStrategy>(kotlinx.serialization.json/JsonNamingStrategy?) // kotlinx.serialization.json/JsonBuilder.namingStrategy.<set-namingStrategy>|<set-namingStrategy>(kotlinx.serialization.json.JsonNamingStrategy?){}[0]
+    final var prettyPrint // kotlinx.serialization.json/JsonBuilder.prettyPrint|{}prettyPrint[0]
+        final fun <get-prettyPrint>(): kotlin/Boolean // kotlinx.serialization.json/JsonBuilder.prettyPrint.<get-prettyPrint>|<get-prettyPrint>(){}[0]
+        final fun <set-prettyPrint>(kotlin/Boolean) // kotlinx.serialization.json/JsonBuilder.prettyPrint.<set-prettyPrint>|<set-prettyPrint>(kotlin.Boolean){}[0]
+    final var prettyPrintIndent // kotlinx.serialization.json/JsonBuilder.prettyPrintIndent|{}prettyPrintIndent[0]
+        final fun <get-prettyPrintIndent>(): kotlin/String // kotlinx.serialization.json/JsonBuilder.prettyPrintIndent.<get-prettyPrintIndent>|<get-prettyPrintIndent>(){}[0]
+        final fun <set-prettyPrintIndent>(kotlin/String) // kotlinx.serialization.json/JsonBuilder.prettyPrintIndent.<set-prettyPrintIndent>|<set-prettyPrintIndent>(kotlin.String){}[0]
+    final var serializersModule // kotlinx.serialization.json/JsonBuilder.serializersModule|{}serializersModule[0]
+        final fun <get-serializersModule>(): kotlinx.serialization.modules/SerializersModule // kotlinx.serialization.json/JsonBuilder.serializersModule.<get-serializersModule>|<get-serializersModule>(){}[0]
+        final fun <set-serializersModule>(kotlinx.serialization.modules/SerializersModule) // kotlinx.serialization.json/JsonBuilder.serializersModule.<set-serializersModule>|<set-serializersModule>(kotlinx.serialization.modules.SerializersModule){}[0]
+    final var useAlternativeNames // kotlinx.serialization.json/JsonBuilder.useAlternativeNames|{}useAlternativeNames[0]
+        final fun <get-useAlternativeNames>(): kotlin/Boolean // kotlinx.serialization.json/JsonBuilder.useAlternativeNames.<get-useAlternativeNames>|<get-useAlternativeNames>(){}[0]
+        final fun <set-useAlternativeNames>(kotlin/Boolean) // kotlinx.serialization.json/JsonBuilder.useAlternativeNames.<set-useAlternativeNames>|<set-useAlternativeNames>(kotlin.Boolean){}[0]
+    final var useArrayPolymorphism // kotlinx.serialization.json/JsonBuilder.useArrayPolymorphism|{}useArrayPolymorphism[0]
+        final fun <get-useArrayPolymorphism>(): kotlin/Boolean // kotlinx.serialization.json/JsonBuilder.useArrayPolymorphism.<get-useArrayPolymorphism>|<get-useArrayPolymorphism>(){}[0]
+        final fun <set-useArrayPolymorphism>(kotlin/Boolean) // kotlinx.serialization.json/JsonBuilder.useArrayPolymorphism.<set-useArrayPolymorphism>|<set-useArrayPolymorphism>(kotlin.Boolean){}[0]
+}
+
+final class kotlinx.serialization.json/JsonConfiguration { // kotlinx.serialization.json/JsonConfiguration|null[0]
+    final val allowComments // kotlinx.serialization.json/JsonConfiguration.allowComments|{}allowComments[0]
+        final fun <get-allowComments>(): kotlin/Boolean // kotlinx.serialization.json/JsonConfiguration.allowComments.<get-allowComments>|<get-allowComments>(){}[0]
+    final val allowSpecialFloatingPointValues // kotlinx.serialization.json/JsonConfiguration.allowSpecialFloatingPointValues|{}allowSpecialFloatingPointValues[0]
+        final fun <get-allowSpecialFloatingPointValues>(): kotlin/Boolean // kotlinx.serialization.json/JsonConfiguration.allowSpecialFloatingPointValues.<get-allowSpecialFloatingPointValues>|<get-allowSpecialFloatingPointValues>(){}[0]
+    final val allowStructuredMapKeys // kotlinx.serialization.json/JsonConfiguration.allowStructuredMapKeys|{}allowStructuredMapKeys[0]
+        final fun <get-allowStructuredMapKeys>(): kotlin/Boolean // kotlinx.serialization.json/JsonConfiguration.allowStructuredMapKeys.<get-allowStructuredMapKeys>|<get-allowStructuredMapKeys>(){}[0]
+    final val allowTrailingComma // kotlinx.serialization.json/JsonConfiguration.allowTrailingComma|{}allowTrailingComma[0]
+        final fun <get-allowTrailingComma>(): kotlin/Boolean // kotlinx.serialization.json/JsonConfiguration.allowTrailingComma.<get-allowTrailingComma>|<get-allowTrailingComma>(){}[0]
+    final val classDiscriminator // kotlinx.serialization.json/JsonConfiguration.classDiscriminator|{}classDiscriminator[0]
+        final fun <get-classDiscriminator>(): kotlin/String // kotlinx.serialization.json/JsonConfiguration.classDiscriminator.<get-classDiscriminator>|<get-classDiscriminator>(){}[0]
+    final val coerceInputValues // kotlinx.serialization.json/JsonConfiguration.coerceInputValues|{}coerceInputValues[0]
+        final fun <get-coerceInputValues>(): kotlin/Boolean // kotlinx.serialization.json/JsonConfiguration.coerceInputValues.<get-coerceInputValues>|<get-coerceInputValues>(){}[0]
+    final val decodeEnumsCaseInsensitive // kotlinx.serialization.json/JsonConfiguration.decodeEnumsCaseInsensitive|{}decodeEnumsCaseInsensitive[0]
+        final fun <get-decodeEnumsCaseInsensitive>(): kotlin/Boolean // kotlinx.serialization.json/JsonConfiguration.decodeEnumsCaseInsensitive.<get-decodeEnumsCaseInsensitive>|<get-decodeEnumsCaseInsensitive>(){}[0]
+    final val encodeDefaults // kotlinx.serialization.json/JsonConfiguration.encodeDefaults|{}encodeDefaults[0]
+        final fun <get-encodeDefaults>(): kotlin/Boolean // kotlinx.serialization.json/JsonConfiguration.encodeDefaults.<get-encodeDefaults>|<get-encodeDefaults>(){}[0]
+    final val explicitNulls // kotlinx.serialization.json/JsonConfiguration.explicitNulls|{}explicitNulls[0]
+        final fun <get-explicitNulls>(): kotlin/Boolean // kotlinx.serialization.json/JsonConfiguration.explicitNulls.<get-explicitNulls>|<get-explicitNulls>(){}[0]
+    final val ignoreUnknownKeys // kotlinx.serialization.json/JsonConfiguration.ignoreUnknownKeys|{}ignoreUnknownKeys[0]
+        final fun <get-ignoreUnknownKeys>(): kotlin/Boolean // kotlinx.serialization.json/JsonConfiguration.ignoreUnknownKeys.<get-ignoreUnknownKeys>|<get-ignoreUnknownKeys>(){}[0]
+    final val isLenient // kotlinx.serialization.json/JsonConfiguration.isLenient|{}isLenient[0]
+        final fun <get-isLenient>(): kotlin/Boolean // kotlinx.serialization.json/JsonConfiguration.isLenient.<get-isLenient>|<get-isLenient>(){}[0]
+    final val namingStrategy // kotlinx.serialization.json/JsonConfiguration.namingStrategy|{}namingStrategy[0]
+        final fun <get-namingStrategy>(): kotlinx.serialization.json/JsonNamingStrategy? // kotlinx.serialization.json/JsonConfiguration.namingStrategy.<get-namingStrategy>|<get-namingStrategy>(){}[0]
+    final val prettyPrint // kotlinx.serialization.json/JsonConfiguration.prettyPrint|{}prettyPrint[0]
+        final fun <get-prettyPrint>(): kotlin/Boolean // kotlinx.serialization.json/JsonConfiguration.prettyPrint.<get-prettyPrint>|<get-prettyPrint>(){}[0]
+    final val prettyPrintIndent // kotlinx.serialization.json/JsonConfiguration.prettyPrintIndent|{}prettyPrintIndent[0]
+        final fun <get-prettyPrintIndent>(): kotlin/String // kotlinx.serialization.json/JsonConfiguration.prettyPrintIndent.<get-prettyPrintIndent>|<get-prettyPrintIndent>(){}[0]
+    final val useAlternativeNames // kotlinx.serialization.json/JsonConfiguration.useAlternativeNames|{}useAlternativeNames[0]
+        final fun <get-useAlternativeNames>(): kotlin/Boolean // kotlinx.serialization.json/JsonConfiguration.useAlternativeNames.<get-useAlternativeNames>|<get-useAlternativeNames>(){}[0]
+    final val useArrayPolymorphism // kotlinx.serialization.json/JsonConfiguration.useArrayPolymorphism|{}useArrayPolymorphism[0]
+        final fun <get-useArrayPolymorphism>(): kotlin/Boolean // kotlinx.serialization.json/JsonConfiguration.useArrayPolymorphism.<get-useArrayPolymorphism>|<get-useArrayPolymorphism>(){}[0]
+
+    final var classDiscriminatorMode // kotlinx.serialization.json/JsonConfiguration.classDiscriminatorMode|{}classDiscriminatorMode[0]
+        final fun <get-classDiscriminatorMode>(): kotlinx.serialization.json/ClassDiscriminatorMode // kotlinx.serialization.json/JsonConfiguration.classDiscriminatorMode.<get-classDiscriminatorMode>|<get-classDiscriminatorMode>(){}[0]
+        final fun <set-classDiscriminatorMode>(kotlinx.serialization.json/ClassDiscriminatorMode) // kotlinx.serialization.json/JsonConfiguration.classDiscriminatorMode.<set-classDiscriminatorMode>|<set-classDiscriminatorMode>(kotlinx.serialization.json.ClassDiscriminatorMode){}[0]
+
+    final fun toString(): kotlin/String // kotlinx.serialization.json/JsonConfiguration.toString|toString(){}[0]
+}
+
+final class kotlinx.serialization.json/JsonObject : kotlin.collections/Map<kotlin/String, kotlinx.serialization.json/JsonElement>, kotlinx.serialization.json/JsonElement { // kotlinx.serialization.json/JsonObject|null[0]
+    constructor <init>(kotlin.collections/Map<kotlin/String, kotlinx.serialization.json/JsonElement>) // kotlinx.serialization.json/JsonObject.<init>|<init>(kotlin.collections.Map<kotlin.String,kotlinx.serialization.json.JsonElement>){}[0]
+
+    final val entries // kotlinx.serialization.json/JsonObject.entries|{}entries[0]
+        final fun <get-entries>(): kotlin.collections/Set<kotlin.collections/Map.Entry<kotlin/String, kotlinx.serialization.json/JsonElement>> // kotlinx.serialization.json/JsonObject.entries.<get-entries>|<get-entries>(){}[0]
+    final val keys // kotlinx.serialization.json/JsonObject.keys|{}keys[0]
+        final fun <get-keys>(): kotlin.collections/Set<kotlin/String> // kotlinx.serialization.json/JsonObject.keys.<get-keys>|<get-keys>(){}[0]
+    final val size // kotlinx.serialization.json/JsonObject.size|{}size[0]
+        final fun <get-size>(): kotlin/Int // kotlinx.serialization.json/JsonObject.size.<get-size>|<get-size>(){}[0]
+    final val values // kotlinx.serialization.json/JsonObject.values|{}values[0]
+        final fun <get-values>(): kotlin.collections/Collection<kotlinx.serialization.json/JsonElement> // kotlinx.serialization.json/JsonObject.values.<get-values>|<get-values>(){}[0]
+
+    final fun containsKey(kotlin/String): kotlin/Boolean // kotlinx.serialization.json/JsonObject.containsKey|containsKey(kotlin.String){}[0]
+    final fun containsValue(kotlinx.serialization.json/JsonElement): kotlin/Boolean // kotlinx.serialization.json/JsonObject.containsValue|containsValue(kotlinx.serialization.json.JsonElement){}[0]
+    final fun equals(kotlin/Any?): kotlin/Boolean // kotlinx.serialization.json/JsonObject.equals|equals(kotlin.Any?){}[0]
+    final fun get(kotlin/String): kotlinx.serialization.json/JsonElement? // kotlinx.serialization.json/JsonObject.get|get(kotlin.String){}[0]
+    final fun hashCode(): kotlin/Int // kotlinx.serialization.json/JsonObject.hashCode|hashCode(){}[0]
+    final fun isEmpty(): kotlin/Boolean // kotlinx.serialization.json/JsonObject.isEmpty|isEmpty(){}[0]
+    final fun toString(): kotlin/String // kotlinx.serialization.json/JsonObject.toString|toString(){}[0]
+
+    final object Companion { // kotlinx.serialization.json/JsonObject.Companion|null[0]
+        final fun serializer(): kotlinx.serialization/KSerializer<kotlinx.serialization.json/JsonObject> // kotlinx.serialization.json/JsonObject.Companion.serializer|serializer(){}[0]
+    }
+
+    // Targets: [js]
+    final fun asJsReadonlyMapView(): kotlin.js.collections/JsReadonlyMap<kotlin/String, kotlinx.serialization.json/JsonElement> // kotlinx.serialization.json/JsonObject.asJsReadonlyMapView|asJsReadonlyMapView(){}[0]
+}
+
+final class kotlinx.serialization.json/JsonObjectBuilder { // kotlinx.serialization.json/JsonObjectBuilder|null[0]
+    constructor <init>() // kotlinx.serialization.json/JsonObjectBuilder.<init>|<init>(){}[0]
+
+    final fun build(): kotlinx.serialization.json/JsonObject // kotlinx.serialization.json/JsonObjectBuilder.build|build(){}[0]
+    final fun put(kotlin/String, kotlinx.serialization.json/JsonElement): kotlinx.serialization.json/JsonElement? // kotlinx.serialization.json/JsonObjectBuilder.put|put(kotlin.String;kotlinx.serialization.json.JsonElement){}[0]
+}
+
+sealed class kotlinx.serialization.json/Json : kotlinx.serialization/StringFormat { // kotlinx.serialization.json/Json|null[0]
+    final val configuration // kotlinx.serialization.json/Json.configuration|{}configuration[0]
+        final fun <get-configuration>(): kotlinx.serialization.json/JsonConfiguration // kotlinx.serialization.json/Json.configuration.<get-configuration>|<get-configuration>(){}[0]
+    open val serializersModule // kotlinx.serialization.json/Json.serializersModule|{}serializersModule[0]
+        open fun <get-serializersModule>(): kotlinx.serialization.modules/SerializersModule // kotlinx.serialization.json/Json.serializersModule.<get-serializersModule>|<get-serializersModule>(){}[0]
+
+    final fun <#A1: kotlin/Any?> decodeFromJsonElement(kotlinx.serialization/DeserializationStrategy<#A1>, kotlinx.serialization.json/JsonElement): #A1 // kotlinx.serialization.json/Json.decodeFromJsonElement|decodeFromJsonElement(kotlinx.serialization.DeserializationStrategy<0:0>;kotlinx.serialization.json.JsonElement){0§<kotlin.Any?>}[0]
+    final fun <#A1: kotlin/Any?> decodeFromString(kotlinx.serialization/DeserializationStrategy<#A1>, kotlin/String): #A1 // kotlinx.serialization.json/Json.decodeFromString|decodeFromString(kotlinx.serialization.DeserializationStrategy<0:0>;kotlin.String){0§<kotlin.Any?>}[0]
+    final fun <#A1: kotlin/Any?> encodeToJsonElement(kotlinx.serialization/SerializationStrategy<#A1>, #A1): kotlinx.serialization.json/JsonElement // kotlinx.serialization.json/Json.encodeToJsonElement|encodeToJsonElement(kotlinx.serialization.SerializationStrategy<0:0>;0:0){0§<kotlin.Any?>}[0]
+    final fun <#A1: kotlin/Any?> encodeToString(kotlinx.serialization/SerializationStrategy<#A1>, #A1): kotlin/String // kotlinx.serialization.json/Json.encodeToString|encodeToString(kotlinx.serialization.SerializationStrategy<0:0>;0:0){0§<kotlin.Any?>}[0]
+    final fun parseToJsonElement(kotlin/String): kotlinx.serialization.json/JsonElement // kotlinx.serialization.json/Json.parseToJsonElement|parseToJsonElement(kotlin.String){}[0]
+    final inline fun <#A1: reified kotlin/Any?> decodeFromString(kotlin/String): #A1 // kotlinx.serialization.json/Json.decodeFromString|decodeFromString(kotlin.String){0§<kotlin.Any?>}[0]
+
+    final object Default : kotlinx.serialization.json/Json // kotlinx.serialization.json/Json.Default|null[0]
+}
+
+sealed class kotlinx.serialization.json/JsonElement { // kotlinx.serialization.json/JsonElement|null[0]
+    final object Companion { // kotlinx.serialization.json/JsonElement.Companion|null[0]
+        final fun serializer(): kotlinx.serialization/KSerializer<kotlinx.serialization.json/JsonElement> // kotlinx.serialization.json/JsonElement.Companion.serializer|serializer(){}[0]
+    }
+}
+
+sealed class kotlinx.serialization.json/JsonPrimitive : kotlinx.serialization.json/JsonElement { // kotlinx.serialization.json/JsonPrimitive|null[0]
+    abstract val content // kotlinx.serialization.json/JsonPrimitive.content|{}content[0]
+        abstract fun <get-content>(): kotlin/String // kotlinx.serialization.json/JsonPrimitive.content.<get-content>|<get-content>(){}[0]
+    abstract val isString // kotlinx.serialization.json/JsonPrimitive.isString|{}isString[0]
+        abstract fun <get-isString>(): kotlin/Boolean // kotlinx.serialization.json/JsonPrimitive.isString.<get-isString>|<get-isString>(){}[0]
+
+    open fun toString(): kotlin/String // kotlinx.serialization.json/JsonPrimitive.toString|toString(){}[0]
+
+    final object Companion { // kotlinx.serialization.json/JsonPrimitive.Companion|null[0]
+        final fun serializer(): kotlinx.serialization/KSerializer<kotlinx.serialization.json/JsonPrimitive> // kotlinx.serialization.json/JsonPrimitive.Companion.serializer|serializer(){}[0]
+    }
+}
+
+final object kotlinx.serialization.json/JsonArraySerializer : kotlinx.serialization/KSerializer<kotlinx.serialization.json/JsonArray> { // kotlinx.serialization.json/JsonArraySerializer|null[0]
+    final val descriptor // kotlinx.serialization.json/JsonArraySerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.json/JsonArraySerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+
+    final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlinx.serialization.json/JsonArray // kotlinx.serialization.json/JsonArraySerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
+    final fun serialize(kotlinx.serialization.encoding/Encoder, kotlinx.serialization.json/JsonArray) // kotlinx.serialization.json/JsonArraySerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlinx.serialization.json.JsonArray){}[0]
+}
+
+final object kotlinx.serialization.json/JsonElementSerializer : kotlinx.serialization/KSerializer<kotlinx.serialization.json/JsonElement> { // kotlinx.serialization.json/JsonElementSerializer|null[0]
+    final val descriptor // kotlinx.serialization.json/JsonElementSerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.json/JsonElementSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+
+    final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlinx.serialization.json/JsonElement // kotlinx.serialization.json/JsonElementSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
+    final fun serialize(kotlinx.serialization.encoding/Encoder, kotlinx.serialization.json/JsonElement) // kotlinx.serialization.json/JsonElementSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlinx.serialization.json.JsonElement){}[0]
+}
+
+final object kotlinx.serialization.json/JsonNull : kotlinx.serialization.internal/SerializerFactory, kotlinx.serialization.json/JsonPrimitive { // kotlinx.serialization.json/JsonNull|null[0]
+    final val content // kotlinx.serialization.json/JsonNull.content|{}content[0]
+        final fun <get-content>(): kotlin/String // kotlinx.serialization.json/JsonNull.content.<get-content>|<get-content>(){}[0]
+    final val isString // kotlinx.serialization.json/JsonNull.isString|{}isString[0]
+        final fun <get-isString>(): kotlin/Boolean // kotlinx.serialization.json/JsonNull.isString.<get-isString>|<get-isString>(){}[0]
+
+    final fun serializer(): kotlinx.serialization/KSerializer<kotlinx.serialization.json/JsonNull> // kotlinx.serialization.json/JsonNull.serializer|serializer(){}[0]
+    final fun serializer(kotlin/Array<out kotlinx.serialization/KSerializer<*>>...): kotlinx.serialization/KSerializer<*> // kotlinx.serialization.json/JsonNull.serializer|serializer(kotlin.Array<out|kotlinx.serialization.KSerializer<*>>...){}[0]
+}
+
+final object kotlinx.serialization.json/JsonNullSerializer : kotlinx.serialization/KSerializer<kotlinx.serialization.json/JsonNull> { // kotlinx.serialization.json/JsonNullSerializer|null[0]
+    final val descriptor // kotlinx.serialization.json/JsonNullSerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.json/JsonNullSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+
+    final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlinx.serialization.json/JsonNull // kotlinx.serialization.json/JsonNullSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
+    final fun serialize(kotlinx.serialization.encoding/Encoder, kotlinx.serialization.json/JsonNull) // kotlinx.serialization.json/JsonNullSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlinx.serialization.json.JsonNull){}[0]
+}
+
+final object kotlinx.serialization.json/JsonObjectSerializer : kotlinx.serialization/KSerializer<kotlinx.serialization.json/JsonObject> { // kotlinx.serialization.json/JsonObjectSerializer|null[0]
+    final val descriptor // kotlinx.serialization.json/JsonObjectSerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.json/JsonObjectSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+
+    final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlinx.serialization.json/JsonObject // kotlinx.serialization.json/JsonObjectSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
+    final fun serialize(kotlinx.serialization.encoding/Encoder, kotlinx.serialization.json/JsonObject) // kotlinx.serialization.json/JsonObjectSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlinx.serialization.json.JsonObject){}[0]
+}
+
+final object kotlinx.serialization.json/JsonPrimitiveSerializer : kotlinx.serialization/KSerializer<kotlinx.serialization.json/JsonPrimitive> { // kotlinx.serialization.json/JsonPrimitiveSerializer|null[0]
+    final val descriptor // kotlinx.serialization.json/JsonPrimitiveSerializer.descriptor|{}descriptor[0]
+        final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // kotlinx.serialization.json/JsonPrimitiveSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
+
+    final fun deserialize(kotlinx.serialization.encoding/Decoder): kotlinx.serialization.json/JsonPrimitive // kotlinx.serialization.json/JsonPrimitiveSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
+    final fun serialize(kotlinx.serialization.encoding/Encoder, kotlinx.serialization.json/JsonPrimitive) // kotlinx.serialization.json/JsonPrimitiveSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;kotlinx.serialization.json.JsonPrimitive){}[0]
+}
+
+final val kotlinx.serialization.json.internal/ESCAPE_STRINGS // kotlinx.serialization.json.internal/ESCAPE_STRINGS|{}ESCAPE_STRINGS[0]
+    final fun <get-ESCAPE_STRINGS>(): kotlin/Array<kotlin/String?> // kotlinx.serialization.json.internal/ESCAPE_STRINGS.<get-ESCAPE_STRINGS>|<get-ESCAPE_STRINGS>(){}[0]
+final val kotlinx.serialization.json/boolean // kotlinx.serialization.json/boolean|@kotlinx.serialization.json.JsonPrimitive{}boolean[0]
+    final fun (kotlinx.serialization.json/JsonPrimitive).<get-boolean>(): kotlin/Boolean // kotlinx.serialization.json/boolean.<get-boolean>|<get-boolean>@kotlinx.serialization.json.JsonPrimitive(){}[0]
+final val kotlinx.serialization.json/booleanOrNull // kotlinx.serialization.json/booleanOrNull|@kotlinx.serialization.json.JsonPrimitive{}booleanOrNull[0]
+    final fun (kotlinx.serialization.json/JsonPrimitive).<get-booleanOrNull>(): kotlin/Boolean? // kotlinx.serialization.json/booleanOrNull.<get-booleanOrNull>|<get-booleanOrNull>@kotlinx.serialization.json.JsonPrimitive(){}[0]
+final val kotlinx.serialization.json/contentOrNull // kotlinx.serialization.json/contentOrNull|@kotlinx.serialization.json.JsonPrimitive{}contentOrNull[0]
+    final fun (kotlinx.serialization.json/JsonPrimitive).<get-contentOrNull>(): kotlin/String? // kotlinx.serialization.json/contentOrNull.<get-contentOrNull>|<get-contentOrNull>@kotlinx.serialization.json.JsonPrimitive(){}[0]
+final val kotlinx.serialization.json/double // kotlinx.serialization.json/double|@kotlinx.serialization.json.JsonPrimitive{}double[0]
+    final fun (kotlinx.serialization.json/JsonPrimitive).<get-double>(): kotlin/Double // kotlinx.serialization.json/double.<get-double>|<get-double>@kotlinx.serialization.json.JsonPrimitive(){}[0]
+final val kotlinx.serialization.json/doubleOrNull // kotlinx.serialization.json/doubleOrNull|@kotlinx.serialization.json.JsonPrimitive{}doubleOrNull[0]
+    final fun (kotlinx.serialization.json/JsonPrimitive).<get-doubleOrNull>(): kotlin/Double? // kotlinx.serialization.json/doubleOrNull.<get-doubleOrNull>|<get-doubleOrNull>@kotlinx.serialization.json.JsonPrimitive(){}[0]
+final val kotlinx.serialization.json/float // kotlinx.serialization.json/float|@kotlinx.serialization.json.JsonPrimitive{}float[0]
+    final fun (kotlinx.serialization.json/JsonPrimitive).<get-float>(): kotlin/Float // kotlinx.serialization.json/float.<get-float>|<get-float>@kotlinx.serialization.json.JsonPrimitive(){}[0]
+final val kotlinx.serialization.json/floatOrNull // kotlinx.serialization.json/floatOrNull|@kotlinx.serialization.json.JsonPrimitive{}floatOrNull[0]
+    final fun (kotlinx.serialization.json/JsonPrimitive).<get-floatOrNull>(): kotlin/Float? // kotlinx.serialization.json/floatOrNull.<get-floatOrNull>|<get-floatOrNull>@kotlinx.serialization.json.JsonPrimitive(){}[0]
+final val kotlinx.serialization.json/int // kotlinx.serialization.json/int|@kotlinx.serialization.json.JsonPrimitive{}int[0]
+    final fun (kotlinx.serialization.json/JsonPrimitive).<get-int>(): kotlin/Int // kotlinx.serialization.json/int.<get-int>|<get-int>@kotlinx.serialization.json.JsonPrimitive(){}[0]
+final val kotlinx.serialization.json/intOrNull // kotlinx.serialization.json/intOrNull|@kotlinx.serialization.json.JsonPrimitive{}intOrNull[0]
+    final fun (kotlinx.serialization.json/JsonPrimitive).<get-intOrNull>(): kotlin/Int? // kotlinx.serialization.json/intOrNull.<get-intOrNull>|<get-intOrNull>@kotlinx.serialization.json.JsonPrimitive(){}[0]
+final val kotlinx.serialization.json/jsonArray // kotlinx.serialization.json/jsonArray|@kotlinx.serialization.json.JsonElement{}jsonArray[0]
+    final fun (kotlinx.serialization.json/JsonElement).<get-jsonArray>(): kotlinx.serialization.json/JsonArray // kotlinx.serialization.json/jsonArray.<get-jsonArray>|<get-jsonArray>@kotlinx.serialization.json.JsonElement(){}[0]
+final val kotlinx.serialization.json/jsonNull // kotlinx.serialization.json/jsonNull|@kotlinx.serialization.json.JsonElement{}jsonNull[0]
+    final fun (kotlinx.serialization.json/JsonElement).<get-jsonNull>(): kotlinx.serialization.json/JsonNull // kotlinx.serialization.json/jsonNull.<get-jsonNull>|<get-jsonNull>@kotlinx.serialization.json.JsonElement(){}[0]
+final val kotlinx.serialization.json/jsonObject // kotlinx.serialization.json/jsonObject|@kotlinx.serialization.json.JsonElement{}jsonObject[0]
+    final fun (kotlinx.serialization.json/JsonElement).<get-jsonObject>(): kotlinx.serialization.json/JsonObject // kotlinx.serialization.json/jsonObject.<get-jsonObject>|<get-jsonObject>@kotlinx.serialization.json.JsonElement(){}[0]
+final val kotlinx.serialization.json/jsonPrimitive // kotlinx.serialization.json/jsonPrimitive|@kotlinx.serialization.json.JsonElement{}jsonPrimitive[0]
+    final fun (kotlinx.serialization.json/JsonElement).<get-jsonPrimitive>(): kotlinx.serialization.json/JsonPrimitive // kotlinx.serialization.json/jsonPrimitive.<get-jsonPrimitive>|<get-jsonPrimitive>@kotlinx.serialization.json.JsonElement(){}[0]
+final val kotlinx.serialization.json/long // kotlinx.serialization.json/long|@kotlinx.serialization.json.JsonPrimitive{}long[0]
+    final fun (kotlinx.serialization.json/JsonPrimitive).<get-long>(): kotlin/Long // kotlinx.serialization.json/long.<get-long>|<get-long>@kotlinx.serialization.json.JsonPrimitive(){}[0]
+final val kotlinx.serialization.json/longOrNull // kotlinx.serialization.json/longOrNull|@kotlinx.serialization.json.JsonPrimitive{}longOrNull[0]
+    final fun (kotlinx.serialization.json/JsonPrimitive).<get-longOrNull>(): kotlin/Long? // kotlinx.serialization.json/longOrNull.<get-longOrNull>|<get-longOrNull>@kotlinx.serialization.json.JsonPrimitive(){}[0]
+
+final fun (kotlinx.serialization.json/JsonArrayBuilder).kotlinx.serialization.json/add(kotlin/Boolean?): kotlin/Boolean // kotlinx.serialization.json/add|[email protected](kotlin.Boolean?){}[0]
+final fun (kotlinx.serialization.json/JsonArrayBuilder).kotlinx.serialization.json/add(kotlin/Nothing?): kotlin/Boolean // kotlinx.serialization.json/add|[email protected](kotlin.Nothing?){}[0]
+final fun (kotlinx.serialization.json/JsonArrayBuilder).kotlinx.serialization.json/add(kotlin/Number?): kotlin/Boolean // kotlinx.serialization.json/add|[email protected](kotlin.Number?){}[0]
+final fun (kotlinx.serialization.json/JsonArrayBuilder).kotlinx.serialization.json/add(kotlin/String?): kotlin/Boolean // kotlinx.serialization.json/add|[email protected](kotlin.String?){}[0]
+final fun (kotlinx.serialization.json/JsonArrayBuilder).kotlinx.serialization.json/addAll(kotlin.collections/Collection<kotlin/Boolean?>): kotlin/Boolean // kotlinx.serialization.json/addAll|[email protected](kotlin.collections.Collection<kotlin.Boolean?>){}[0]
+final fun (kotlinx.serialization.json/JsonArrayBuilder).kotlinx.serialization.json/addAll(kotlin.collections/Collection<kotlin/Number?>): kotlin/Boolean // kotlinx.serialization.json/addAll|[email protected](kotlin.collections.Collection<kotlin.Number?>){}[0]
+final fun (kotlinx.serialization.json/JsonArrayBuilder).kotlinx.serialization.json/addAll(kotlin.collections/Collection<kotlin/String?>): kotlin/Boolean // kotlinx.serialization.json/addAll|[email protected](kotlin.collections.Collection<kotlin.String?>){}[0]
+final fun (kotlinx.serialization.json/JsonArrayBuilder).kotlinx.serialization.json/addJsonArray(kotlin/Function1<kotlinx.serialization.json/JsonArrayBuilder, kotlin/Unit>): kotlin/Boolean // kotlinx.serialization.json/addJsonArray|[email protected](kotlin.Function1<kotlinx.serialization.json.JsonArrayBuilder,kotlin.Unit>){}[0]
+final fun (kotlinx.serialization.json/JsonArrayBuilder).kotlinx.serialization.json/addJsonObject(kotlin/Function1<kotlinx.serialization.json/JsonObjectBuilder, kotlin/Unit>): kotlin/Boolean // kotlinx.serialization.json/addJsonObject|[email protected](kotlin.Function1<kotlinx.serialization.json.JsonObjectBuilder,kotlin.Unit>){}[0]
+final fun (kotlinx.serialization.json/JsonObjectBuilder).kotlinx.serialization.json/put(kotlin/String, kotlin/Boolean?): kotlinx.serialization.json/JsonElement? // kotlinx.serialization.json/put|[email protected](kotlin.String;kotlin.Boolean?){}[0]
+final fun (kotlinx.serialization.json/JsonObjectBuilder).kotlinx.serialization.json/put(kotlin/String, kotlin/Nothing?): kotlinx.serialization.json/JsonElement? // kotlinx.serialization.json/put|[email protected](kotlin.String;kotlin.Nothing?){}[0]
+final fun (kotlinx.serialization.json/JsonObjectBuilder).kotlinx.serialization.json/put(kotlin/String, kotlin/Number?): kotlinx.serialization.json/JsonElement? // kotlinx.serialization.json/put|[email protected](kotlin.String;kotlin.Number?){}[0]
+final fun (kotlinx.serialization.json/JsonObjectBuilder).kotlinx.serialization.json/put(kotlin/String, kotlin/String?): kotlinx.serialization.json/JsonElement? // kotlinx.serialization.json/put|[email protected](kotlin.String;kotlin.String?){}[0]
+final fun (kotlinx.serialization.json/JsonObjectBuilder).kotlinx.serialization.json/putJsonArray(kotlin/String, kotlin/Function1<kotlinx.serialization.json/JsonArrayBuilder, kotlin/Unit>): kotlinx.serialization.json/JsonElement? // kotlinx.serialization.json/putJsonArray|[email protected](kotlin.String;kotlin.Function1<kotlinx.serialization.json.JsonArrayBuilder,kotlin.Unit>){}[0]
+final fun (kotlinx.serialization.json/JsonObjectBuilder).kotlinx.serialization.json/putJsonObject(kotlin/String, kotlin/Function1<kotlinx.serialization.json/JsonObjectBuilder, kotlin/Unit>): kotlinx.serialization.json/JsonElement? // kotlinx.serialization.json/putJsonObject|[email protected](kotlin.String;kotlin.Function1<kotlinx.serialization.json.JsonObjectBuilder,kotlin.Unit>){}[0]
+final fun <#A: kotlin/Any?> kotlinx.serialization.json.internal/decodeByReader(kotlinx.serialization.json/Json, kotlinx.serialization/DeserializationStrategy<#A>, kotlinx.serialization.json.internal/InternalJsonReader): #A // kotlinx.serialization.json.internal/decodeByReader|decodeByReader(kotlinx.serialization.json.Json;kotlinx.serialization.DeserializationStrategy<0:0>;kotlinx.serialization.json.internal.InternalJsonReader){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> kotlinx.serialization.json.internal/decodeStringToJsonTree(kotlinx.serialization.json/Json, kotlinx.serialization/DeserializationStrategy<#A>, kotlin/String): kotlinx.serialization.json/JsonElement // kotlinx.serialization.json.internal/decodeStringToJsonTree|decodeStringToJsonTree(kotlinx.serialization.json.Json;kotlinx.serialization.DeserializationStrategy<0:0>;kotlin.String){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> kotlinx.serialization.json.internal/decodeToSequenceByReader(kotlinx.serialization.json/Json, kotlinx.serialization.json.internal/InternalJsonReader, kotlinx.serialization/DeserializationStrategy<#A>, kotlinx.serialization.json/DecodeSequenceMode = ...): kotlin.sequences/Sequence<#A> // kotlinx.serialization.json.internal/decodeToSequenceByReader|decodeToSequenceByReader(kotlinx.serialization.json.Json;kotlinx.serialization.json.internal.InternalJsonReader;kotlinx.serialization.DeserializationStrategy<0:0>;kotlinx.serialization.json.DecodeSequenceMode){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> kotlinx.serialization.json.internal/encodeByWriter(kotlinx.serialization.json/Json, kotlinx.serialization.json.internal/InternalJsonWriter, kotlinx.serialization/SerializationStrategy<#A>, #A) // kotlinx.serialization.json.internal/encodeByWriter|encodeByWriter(kotlinx.serialization.json.Json;kotlinx.serialization.json.internal.InternalJsonWriter;kotlinx.serialization.SerializationStrategy<0:0>;0:0){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> kotlinx.serialization.json.internal/readJson(kotlinx.serialization.json/Json, kotlinx.serialization.json/JsonElement, kotlinx.serialization/DeserializationStrategy<#A>): #A // kotlinx.serialization.json.internal/readJson|readJson(kotlinx.serialization.json.Json;kotlinx.serialization.json.JsonElement;kotlinx.serialization.DeserializationStrategy<0:0>){0§<kotlin.Any?>}[0]
+final fun <#A: kotlin/Any?> kotlinx.serialization.json.internal/writeJson(kotlinx.serialization.json/Json, #A, kotlinx.serialization/SerializationStrategy<#A>): kotlinx.serialization.json/JsonElement // kotlinx.serialization.json.internal/writeJson|writeJson(kotlinx.serialization.json.Json;0:0;kotlinx.serialization.SerializationStrategy<0:0>){0§<kotlin.Any?>}[0]
+final fun kotlinx.serialization.json/Json(kotlinx.serialization.json/Json = ..., kotlin/Function1<kotlinx.serialization.json/JsonBuilder, kotlin/Unit>): kotlinx.serialization.json/Json // kotlinx.serialization.json/Json|Json(kotlinx.serialization.json.Json;kotlin.Function1<kotlinx.serialization.json.JsonBuilder,kotlin.Unit>){}[0]
+final fun kotlinx.serialization.json/JsonPrimitive(kotlin/Boolean?): kotlinx.serialization.json/JsonPrimitive // kotlinx.serialization.json/JsonPrimitive|JsonPrimitive(kotlin.Boolean?){}[0]
+final fun kotlinx.serialization.json/JsonPrimitive(kotlin/Nothing?): kotlinx.serialization.json/JsonNull // kotlinx.serialization.json/JsonPrimitive|JsonPrimitive(kotlin.Nothing?){}[0]
+final fun kotlinx.serialization.json/JsonPrimitive(kotlin/Number?): kotlinx.serialization.json/JsonPrimitive // kotlinx.serialization.json/JsonPrimitive|JsonPrimitive(kotlin.Number?){}[0]
+final fun kotlinx.serialization.json/JsonPrimitive(kotlin/String?): kotlinx.serialization.json/JsonPrimitive // kotlinx.serialization.json/JsonPrimitive|JsonPrimitive(kotlin.String?){}[0]
+final fun kotlinx.serialization.json/JsonPrimitive(kotlin/UByte): kotlinx.serialization.json/JsonPrimitive // kotlinx.serialization.json/JsonPrimitive|JsonPrimitive(kotlin.UByte){}[0]
+final fun kotlinx.serialization.json/JsonPrimitive(kotlin/UInt): kotlinx.serialization.json/JsonPrimitive // kotlinx.serialization.json/JsonPrimitive|JsonPrimitive(kotlin.UInt){}[0]
+final fun kotlinx.serialization.json/JsonPrimitive(kotlin/ULong): kotlinx.serialization.json/JsonPrimitive // kotlinx.serialization.json/JsonPrimitive|JsonPrimitive(kotlin.ULong){}[0]
+final fun kotlinx.serialization.json/JsonPrimitive(kotlin/UShort): kotlinx.serialization.json/JsonPrimitive // kotlinx.serialization.json/JsonPrimitive|JsonPrimitive(kotlin.UShort){}[0]
+final fun kotlinx.serialization.json/JsonUnquotedLiteral(kotlin/String?): kotlinx.serialization.json/JsonPrimitive // kotlinx.serialization.json/JsonUnquotedLiteral|JsonUnquotedLiteral(kotlin.String?){}[0]
+final fun kotlinx.serialization.json/unexpectedJson(kotlin/String, kotlin/String): kotlin/Nothing // kotlinx.serialization.json/unexpectedJson|unexpectedJson(kotlin.String;kotlin.String){}[0]
+final inline fun <#A: reified kotlin/Any?> (kotlinx.serialization.json/Json).kotlinx.serialization.json/decodeFromJsonElement(kotlinx.serialization.json/JsonElement): #A // kotlinx.serialization.json/decodeFromJsonElement|[email protected](kotlinx.serialization.json.JsonElement){0§<kotlin.Any?>}[0]
+final inline fun <#A: reified kotlin/Any?> (kotlinx.serialization.json/Json).kotlinx.serialization.json/encodeToJsonElement(#A): kotlinx.serialization.json/JsonElement // kotlinx.serialization.json/encodeToJsonElement|[email protected](0:0){0§<kotlin.Any?>}[0]
+final inline fun <#A: reified kotlin/Any?> kotlinx.serialization.json.internal/decodeToSequenceByReader(kotlinx.serialization.json/Json, kotlinx.serialization.json.internal/InternalJsonReader, kotlinx.serialization.json/DecodeSequenceMode = ...): kotlin.sequences/Sequence<#A> // kotlinx.serialization.json.internal/decodeToSequenceByReader|decodeToSequenceByReader(kotlinx.serialization.json.Json;kotlinx.serialization.json.internal.InternalJsonReader;kotlinx.serialization.json.DecodeSequenceMode){0§<kotlin.Any?>}[0]
+final inline fun kotlinx.serialization.json/buildJsonArray(kotlin/Function1<kotlinx.serialization.json/JsonArrayBuilder, kotlin/Unit>): kotlinx.serialization.json/JsonArray // kotlinx.serialization.json/buildJsonArray|buildJsonArray(kotlin.Function1<kotlinx.serialization.json.JsonArrayBuilder,kotlin.Unit>){}[0]
+final inline fun kotlinx.serialization.json/buildJsonObject(kotlin/Function1<kotlinx.serialization.json/JsonObjectBuilder, kotlin/Unit>): kotlinx.serialization.json/JsonObject // kotlinx.serialization.json/buildJsonObject|buildJsonObject(kotlin.Function1<kotlinx.serialization.json.JsonObjectBuilder,kotlin.Unit>){}[0]
+
+// Targets: [js]
+final fun <#A: kotlin/Any?> (kotlinx.serialization.json/Json).kotlinx.serialization.json/decodeFromDynamic(kotlinx.serialization/DeserializationStrategy<#A>, dynamic): #A // kotlinx.serialization.json/decodeFromDynamic|[email protected](kotlinx.serialization.DeserializationStrategy<0:0>;<dynamic>){0§<kotlin.Any?>}[0]
+
+// Targets: [js]
+final fun <#A: kotlin/Any?> (kotlinx.serialization.json/Json).kotlinx.serialization.json/encodeToDynamic(kotlinx.serialization/SerializationStrategy<#A>, #A): dynamic // kotlinx.serialization.json/encodeToDynamic|[email protected](kotlinx.serialization.SerializationStrategy<0:0>;0:0){0§<kotlin.Any?>}[0]
+
+// Targets: [js]
+final inline fun <#A: reified kotlin/Any?> (kotlinx.serialization.json/Json).kotlinx.serialization.json/decodeFromDynamic(dynamic): #A // kotlinx.serialization.json/decodeFromDynamic|[email protected](<dynamic>){0§<kotlin.Any?>}[0]
+
+// Targets: [js]
+final inline fun <#A: reified kotlin/Any?> (kotlinx.serialization.json/Json).kotlinx.serialization.json/encodeToDynamic(#A): dynamic // kotlinx.serialization.json/encodeToDynamic|[email protected](0:0){0§<kotlin.Any?>}[0]
diff --git a/formats/json/build.gradle b/formats/json/build.gradle
deleted file mode 100644
index d35bcb5..0000000
--- a/formats/json/build.gradle
+++ /dev/null
@@ -1,58 +0,0 @@
-import static KotlinVersion.isKotlinVersionAtLeast
-
-/*
- * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-apply plugin: 'kotlin-multiplatform'
-apply plugin: 'kotlinx-serialization'
-apply from: rootProject.file("gradle/native-targets.gradle")
-apply from: rootProject.file("gradle/configure-source-sets.gradle")
-
-// disable kover tasks because there are no tests in the project
-tasks.named("koverHtmlReport") {
-    enabled = false
-}
-tasks.named("koverXmlReport") {
-    enabled = false
-}
-tasks.named("koverVerify") {
-    enabled = false
-}
-
-kotlin {
-    sourceSets {
-        configureEach {
-            languageSettings {
-                optIn("kotlinx.serialization.internal.CoreFriendModuleApi")
-                optIn("kotlinx.serialization.json.internal.JsonFriendModuleApi")
-            }
-        }
-        commonMain {
-            dependencies {
-                api project(":kotlinx-serialization-core")
-            }
-        }
-        jsWasmMain {
-            dependsOn(sourceSets.commonMain)
-        }
-        jsMain {
-            dependsOn(sourceSets.jsWasmMain)
-        }
-        wasmJsMain {
-            dependsOn(sourceSets.jsWasmMain)
-        }
-        wasmWasiMain {
-            dependsOn(sourceSets.jsWasmMain)
-        }
-    }
-}
-
-Java9Modularity.configureJava9ModuleInfo(project)
-
-// This task should be disabled because of no need to build and publish intermediate JsWasm sourceset
-tasks.whenTaskAdded { task ->
-    if (task.name == 'compileJsWasmMainKotlinMetadata') {
-        task.enabled = false
-    }
-}
diff --git a/formats/json/build.gradle.kts b/formats/json/build.gradle.kts
new file mode 100644
index 0000000..db48d95
--- /dev/null
+++ b/formats/json/build.gradle.kts
@@ -0,0 +1,62 @@
+import Java9Modularity.configureJava9ModuleInfo
+import org.jetbrains.kotlin.gradle.tasks.*
+
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+plugins {
+    kotlin("multiplatform")
+    alias(libs.plugins.serialization)
+
+    id("native-targets-conventions")
+    id("source-sets-conventions")
+}
+
+// disable kover tasks because there are no tests in the project
+tasks.named("koverHtmlReport") {
+    enabled = false
+}
+tasks.named("koverXmlReport") {
+    enabled = false
+}
+tasks.named("koverVerify") {
+    enabled = false
+}
+
+kotlin {
+    sourceSets {
+        configureEach {
+            languageSettings {
+                optIn("kotlinx.serialization.internal.CoreFriendModuleApi")
+                optIn("kotlinx.serialization.json.internal.JsonFriendModuleApi")
+            }
+        }
+        commonMain {
+            dependencies {
+                api(project(":kotlinx-serialization-core"))
+            }
+        }
+        register("jsWasmMain") {
+            dependsOn(commonMain.get())
+        }
+        named("jsMain") {
+            dependsOn(named("jsWasmMain").get())
+        }
+        named("wasmJsMain") {
+            dependsOn(named("jsWasmMain").get())
+        }
+        named("wasmWasiMain") {
+            dependsOn(named("jsWasmMain").get())
+        }
+    }
+}
+
+// This task should be disabled because of no need to build and publish intermediate JsWasm sourceset
+tasks.whenTaskAdded {
+    if (name == "compileJsWasmMainKotlinMetadata") {
+        enabled = false
+    }
+}
+
+configureJava9ModuleInfo()
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/Json.kt b/formats/json/commonMain/src/kotlinx/serialization/json/Json.kt
index 2a144fe..fe6b094 100644
--- a/formats/json/commonMain/src/kotlinx/serialization/json/Json.kt
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/Json.kt
@@ -1,5 +1,5 @@
 /*
- * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
  */
 
 package kotlinx.serialization.json
@@ -27,29 +27,48 @@
  * Example of usage:
  * ```
  * @Serializable
- * class DataHolder(val id: Int, val data: String, val extensions: JsonElement)
+ * data class Data(val id: Int, val data: String, val extensions: JsonElement)
  *
- * val json = Json
- * val instance = DataHolder(42, "some data", buildJsonObject { put("additional key", "value") }
+ * val json = Json { ignoreUnknownKeys = true }
+ * val instance = Data(42, "some data", buildJsonObject { put("key", "value") })
  *
- * // Plain StringFormat usage
- * val stringOutput: String = json.encodeToString(instance)
+ * // Plain Json usage: returns '{"id": 42, "some data", "extensions": {"key": "value" } }'
+ * val jsonString: String = json.encodeToString(instance)
  *
- * // JsonElement serialization specific for JSON only
- * val jsonTree: JsonElement = json.encodeToJsonElement(instance)
+ * // JsonElement serialization, specific for JSON format
+ * val jsonElement: JsonElement = json.encodeToJsonElement(instance)
  *
  * // Deserialize from string
- * val deserialized: DataHolder = json.decodeFromString<DataHolder>(stringOutput)
+ * val deserialized: Data = json.decodeFromString<Data>(jsonString)
  *
- * // Deserialize from json tree, JSON-specific
- * val deserializedFromTree: DataHolder = json.decodeFromJsonElement<DataHolder>(jsonTree)
+ * // Deserialize from json element, JSON-specific
+ * val deserializedFromElement: Data = json.decodeFromJsonElement<Data>(jsonElement)
  *
  *  // Deserialize from string to JSON tree, JSON-specific
- *  val deserializedToTree: JsonElement = json.parseToJsonElement(stringOutput)
+ * val deserializedElement: JsonElement = json.parseToJsonElement(jsonString)
+ *
+ * // Deserialize a stream of a single item from an input stream
+ * val sequence = Json.decodeToSequence<Data>(ByteArrayInputStream(jsonString.encodeToByteArray()))
+ * for (item in sequence) {
+ *     println(item) // Prints deserialized Data value
+ * }
  * ```
  *
  * Json instance also exposes its [configuration] that can be used in custom serializers
  * that rely on [JsonDecoder] and [JsonEncoder] for customizable behaviour.
+ *
+ * Json format configuration can be refined using the corresponding constructor:
+ * ```
+ * val defaultJson = Json {
+ *     encodeDefaults = true
+ *     ignoreUnknownKeys = true
+ * }
+ * // Will inherit the properties of defaultJson
+ * val debugEndpointJson = Json(defaultJson) {
+ *     // ignoreUnknownKeys and encodeDefaults are set to true
+ *     prettyPrint = true
+ * }
+ * ```
  */
 public sealed class Json(
     public val configuration: JsonConfiguration,
@@ -65,13 +84,38 @@
 
     /**
      * The default instance of [Json] with default configuration.
+     *
+     * Example of usage:
+     * ```
+     * @Serializable
+     * class Project(val name: String, val language: String)
+     *
+     * val data = Project("kotlinx.serialization", "Kotlin")
+     * // Prints {"name":"kotlinx.serialization","language":"Kotlin"}
+     * println(Json.encodeToString(data))
+     * ```
      */
-    @ThreadLocal // to support caching
+    @ThreadLocal // to support caching on K/N
     @OptIn(ExperimentalSerializationApi::class)
     public companion object Default : Json(JsonConfiguration(), EmptySerializersModule())
 
     /**
      * Serializes the [value] into an equivalent JSON using the given [serializer].
+     * This method is recommended to be used with an explicit serializer (e.g. the custom or third-party one),
+     * otherwise the `encodeToString(value: T)` version might be preferred as the most concise one.
+     *
+     * Example of usage:
+     * ```
+     * @Serializable
+     * class Project(val name: String, val language: String)
+     *
+     * val data = Project("kotlinx.serialization", "Kotlin")
+     *
+     * // Prints {"name":"kotlinx.serialization","language":"Kotlin"}
+     * println(Json.encodeToString(Project.serializer(), data))
+     * // The same as Json.encodeToString<T>(value: T) overload
+     * println(Json.encodeToString(data))
+     * ```
      *
      * @throws [SerializationException] if the given value cannot be serialized to JSON.
      */
@@ -88,6 +132,13 @@
     /**
      * Decodes and deserializes the given JSON [string] to the value of type [T] using deserializer
      * retrieved from the reified type parameter.
+     * Example:
+     * ```
+     * @Serializable
+     * data class Project(val name: String, val language: String)
+     * //  Project(name=kotlinx.serialization, language=Kotlin)
+     * println(Json.decodeFromString<Project>("""{"name":"kotlinx.serialization","language":"Kotlin"}"""))
+     * ```
      *
      * @throws SerializationException in case of any decoding-specific error
      * @throws IllegalArgumentException if the decoded input is not a valid instance of [T]
@@ -97,17 +148,25 @@
 
     /**
      * Deserializes the given JSON [string] into a value of type [T] using the given [deserializer].
+     * Example:
+     * ```
+     * @Serializable
+     * data class Project(val name: String, val language: String)
+     * //  Project(name=kotlinx.serialization, language=Kotlin)
+     * println(Json.decodeFromString(Project.serializer(), """{"name":"kotlinx.serialization","language":"Kotlin"}"""))
+     * ```
      *
      * @throws [SerializationException] if the given JSON string is not a valid JSON input for the type [T]
      * @throws [IllegalArgumentException] if the decoded input cannot be represented as a valid instance of type [T]
      */
     public final override fun <T> decodeFromString(deserializer: DeserializationStrategy<T>, @FormatLanguage("json", "", "") string: String): T {
-        val lexer = StringJsonLexer(string)
+        val lexer = StringJsonLexer(this, string)
         val input = StreamingJsonDecoder(this, WriteMode.OBJ, lexer, deserializer.descriptor, null)
         val result = input.decodeSerializableValue(deserializer)
         lexer.expectEof()
         return result
     }
+
     /**
      * Serializes the given [value] into an equivalent [JsonElement] using the given [serializer]
      *
@@ -144,6 +203,21 @@
  * [DecodeSequenceMode] defines a separator between these objects.
  * Typically, these objects are not separated by meaningful characters ([WHITESPACE_SEPARATED]),
  * or the whole stream is a large array of objects separated with commas ([ARRAY_WRAPPED]).
+ *
+ * It is used in `Json.decodeToSequence` family of functions:
+ * ```
+ * @Serializable
+ * data class Game(val name: String)
+ * val input = """{"name": "Gothic"} {"name": "Planescape"} {"name": "Fallout"}"""
+ * // On multiplatform, Okio's Source can be used
+ * val inputStream = ByteArrayInputStream(input.encodeToByteArray())
+ *
+ * val sequence = Json.decodeToSequence<Game>(inputStream, DecodeSequenceMode.WHITESPACE_SEPARATED)
+ * // Prints Game(name=Gothic), Game(name=Planescape) and Game(name=Fallout)
+ * for (game in sequence) {
+ *     println(game)
+ * }
+ * ```
  */
 @ExperimentalSerializationApi
 public enum class DecodeSequenceMode {
@@ -172,7 +246,7 @@
      *
      * Example of `ARRAY_WRAPPED` stream content:
      * ```
-     * """[{"key": "value"}, {"key": "value2"},{"key2": "value2"}]"""
+     * """[{"key": "value"},   {"key": "value2"},{"key2": "value2"}]"""
      * ```
      */
     ARRAY_WRAPPED,
@@ -193,6 +267,19 @@
 
 /**
  * Creates an instance of [Json] configured from the optionally given [Json instance][from] and adjusted with [builderAction].
+ *
+ * Example of usage:
+ * ```
+ * val defaultJson = Json {
+ *     encodeDefaults = true
+ *     ignoreUnknownKeys = true
+ * }
+ * // Will inherit the properties of defaultJson
+ * val debugEndpointJson = Json(defaultJson) {
+ *     // ignoreUnknownKeys and encodeDefaults are set to true
+ *     prettyPrint = true
+ * }
+ * ```
  */
 public fun Json(from: Json = Json.Default, builderAction: JsonBuilder.() -> Unit): Json {
     val builder = JsonBuilder(from)
@@ -222,7 +309,14 @@
     decodeFromJsonElement(serializersModule.serializer(), json)
 
 /**
- * Builder of the [Json] instance provided by `Json { ... }` factory function.
+ * Builder of the [Json] instance provided by `Json { ... }` factory function:
+ *
+ * ```
+ * val json = Json { // this: JsonBuilder
+ *     encodeDefaults = true
+ *     ignoreUnknownKeys = true
+ * }
+ * ```
  */
 @Suppress("unused", "DeprecatedCallableAddReplaceWith")
 @OptIn(ExperimentalSerializationApi::class)
@@ -230,6 +324,21 @@
     /**
      * Specifies whether default values of Kotlin properties should be encoded.
      * `false` by default.
+     *
+     * Example:
+     * ```
+     * @Serializable
+     * class Project(val name: String, val language: String = "kotlin")
+     *
+     * // Prints {"name":"test-project"}
+     * println(Json.encodeToString(Project("test-project")))
+     *
+     * // Prints {"name":"test-project","language":"kotlin"}
+     * val withDefaults = Json { encodeDefaults = true }
+     * println(withDefaults.encodeToString(Project("test-project")))
+     * ```
+     *
+     * This option does not affect decoding.
      */
     public var encodeDefaults: Boolean = json.configuration.encodeDefaults
 
@@ -237,18 +346,71 @@
      * Specifies whether `null` values should be encoded for nullable properties and must be present in JSON object
      * during decoding.
      *
-     * When this flag is disabled properties with `null` values without default are not encoded;
+     * When this flag is disabled properties with `null` values are not encoded;
      * during decoding, the absence of a field value is treated as `null` for nullable properties without a default value.
      *
      * `true` by default.
+     *
+     * It is possible to make decoder treat some invalid input data as the missing field to enhance the functionality of this flag.
+     * See [coerceInputValues] documentation for details.
+     *
+     * Example of usage:
+     * ```
+     * @Serializable
+     * data class Project(val name: String, val description: String?)
+     * val implicitNulls = Json { explicitNulls = false }
+     *
+     * // Encoding
+     * // Prints '{"name":"unknown","description":null}'. null is explicit
+     * println(Json.encodeToString(Project("unknown", null)))
+     * // Prints '{"name":"unknown"}', null is omitted
+     * println(implicitNulls.encodeToString(Project("unknown", null)))
+     *
+     * // Decoding
+     * // Prints Project(name=unknown, description=null)
+     * println(implicitNulls.decodeFromString<Project>("""{"name":"unknown"}"""))
+     * // Fails with "MissingFieldException: Field 'description' is required"
+     * Json.decodeFromString<Project>("""{"name":"unknown"}""")
+     * ```
+     *
+     * Exercise extra caution if you want to use this flag and have non-typical classes with properties
+     * that are nullable, but have default value that is not `null`. In that case, encoding and decoding will not
+     * be symmetrical if `null` is omitted from the output.
+     * Example of such a pitfall:
+     *
+     * ```
+     * @Serializable
+     * data class Example(val nullable: String? = "non-null default")
+     *
+     * val json = Json { explicitNulls = false }
+     *
+     * val original = Example(null)
+     * val s = json.encodeToString(original)
+     * // prints "{}" because of explicitNulls flag
+     * println(s)
+     * val decoded = json.decodeFromString<Example>(s)
+     * // Prints "non-null default" because default value is inserted since `nullable` field is missing in the input
+     * println(decoded.nullable)
+     * println(decoded != original) // true
+     * ```
      */
-    @ExperimentalSerializationApi
     public var explicitNulls: Boolean = json.configuration.explicitNulls
 
     /**
      * Specifies whether encounters of unknown properties in the input JSON
      * should be ignored instead of throwing [SerializationException].
      * `false` by default.
+     *
+     * Example of usage:
+     * ```
+     * @Serializable
+     * data class Project(val name: String)
+     * val withUnknownKeys = Json { ignoreUnknownKeys = true }
+     * // Project(name=unknown), "version" is ignored completely
+     * println(withUnknownKeys.decodeFromString<Project>("""{"name":"unknown", "version": 2.0}"""))
+     * // Fails with "Encountered an unknown key 'version'"
+     * Json.decodeFromString<Project>("""{"name":"unknown", "version": 2.0}""")
+     * ```
      */
     public var ignoreUnknownKeys: Boolean = json.configuration.ignoreUnknownKeys
 
@@ -256,6 +418,9 @@
      * Removes JSON specification restriction (RFC-4627) and makes parser
      * more liberal to the malformed input. In lenient mode, unquoted JSON keys and string values are allowed.
      *
+     * Example of invalid JSON that is accepted with this flag set:
+     * `{key: value}` can be parsed into `@Serializable class Data(val key: String)`.
+     *
      * Its relaxations can be expanded in the future, so that lenient parser becomes even more
      * permissive to invalid values in the input.
      *
@@ -264,21 +429,31 @@
     public var isLenient: Boolean = json.configuration.isLenient
 
     /**
-     * Enables structured objects to be serialized as map keys by
-     * changing serialized form of the map from JSON object (key-value pairs) to flat array like `[k1, v1, k2, v2]`.
+     * Specifies whether resulting JSON should be pretty-printed: formatted and optimized for human readability.
      * `false` by default.
-     */
-    public var allowStructuredMapKeys: Boolean = json.configuration.allowStructuredMapKeys
-
-    /**
-     * Specifies whether resulting JSON should be pretty-printed.
-     *  `false` by default.
+     *
+     * Example of usage:
+     * ```
+     * @Serializable
+     * class Key(val type: String, val opens: String)
+     * val pretty = Json { prettyPrint = true }
+     * /*
+     *  * Prints
+     *  * {
+     *  *     "type": "keycard",
+     *  *     "opens": "secret door"
+     *  * }
+     *  */
+     * println(pretty.encodeToString(Key("keycard", "secret door")))
+     * ```
      */
     public var prettyPrint: Boolean = json.configuration.prettyPrint
 
     /**
-     * Specifies indent string to use with [prettyPrint] mode
+     * Specifies indent string to use with [prettyPrint] mode.
+     * Only whitespace characters are allowed: ' ', '\n', '\r' or '\t'.
      * 4 spaces by default.
+     *
      * Experimentality note: this API is experimental because
      * it is not clear whether this option has compelling use-cases.
      */
@@ -286,30 +461,43 @@
     public var prettyPrintIndent: String = json.configuration.prettyPrintIndent
 
     /**
-     * Enables coercing incorrect JSON values to the default property value (if exists) in the following cases:
+     * Enables coercing incorrect JSON values in the following cases:
+     *
      *   1. JSON value is `null` but the property type is non-nullable.
-     *   2. Property type is an enum type, but JSON value contains unknown enum member.
+     *   2. Property type is an enum type, but JSON value contains an unknown enum member.
+     *
+     * Coerced values are treated as missing; they are replaced either with a default property value if it exists, or with a `null` if [explicitNulls] flag
+     * is set to `false` and a property is nullable (for enums).
+     *
+     * Example of usage:
+     * ```
+     * enum class Choice { A, B, C }
+     *
+     * @Serializable
+     * data class Example1(val a: String = "default", b: Choice = Choice.A, c: Choice? = null)
+     *
+     * val coercingJson = Json { coerceInputValues = true }
+     * // Decodes Example1("default", Choice.A, null) instance
+     * coercingJson.decodeFromString<Example1>("""{"a": null, "b": "unknown", "c": "unknown"}""")
+     *
+     * @Serializable
+     * data class Example2(val c: Choice?)
+     *
+     * val coercingImplicitJson = Json(coercingJson) { explicitNulls = false }
+     * // Decodes Example2(null) instance.
+     * coercingImplicitJson.decodeFromString<Example1>("""{"c": "unknown"}""")
+     * ```
      *
      * `false` by default.
      */
     public var coerceInputValues: Boolean = json.configuration.coerceInputValues
 
     /**
-     * Switches polymorphic serialization to the default array format.
-     * This is an option for legacy JSON format and should not be generally used.
-     * `false` by default.
-     *
-     * This option can only be used if [classDiscriminatorMode] in a default [ClassDiscriminatorMode.POLYMORPHIC] state.
-     */
-    public var useArrayPolymorphism: Boolean = json.configuration.useArrayPolymorphism
-
-    /**
      * Name of the class descriptor property for polymorphic serialization.
-     * "type" by default.
+     * `type` by default.
      */
     public var classDiscriminator: String = json.configuration.classDiscriminator
 
-
     /**
      * Defines which classes and objects should have class discriminator added to the output.
      * [ClassDiscriminatorMode.POLYMORPHIC] by default.
@@ -317,17 +505,10 @@
      * Other modes are generally intended to produce JSON for consumption by third-party libraries,
      * therefore, this setting does not affect the deserialization process.
      */
+    @ExperimentalSerializationApi
     public var classDiscriminatorMode: ClassDiscriminatorMode = json.configuration.classDiscriminatorMode
 
     /**
-     * Removes JSON specification restriction on
-     * special floating-point values such as `NaN` and `Infinity` and enables their serialization and deserialization.
-     * When enabling it, please ensure that the receiving party will be able to encode and decode these special values.
-     * `false` by default.
-     */
-    public var allowSpecialFloatingPointValues: Boolean = json.configuration.allowSpecialFloatingPointValues
-
-    /**
      * Specifies whether Json instance makes use of [JsonNames] annotation.
      *
      * Disabling this flag when one does not use [JsonNames] at all may sometimes result in better performance,
@@ -348,28 +529,30 @@
 
     /**
      * Enables decoding enum values in a case-insensitive manner.
-     * Encoding is not affected.
+     * Encoding is not affected by this option.
      *
-     * This affects both enum serial names and alternative names (specified with the [JsonNames] annotation).
-     * In the following example, string `[VALUE_A, VALUE_B]` will be printed:
+     * It affects both enum serial names and alternative names (specified with the [JsonNames] annotation).
+     * Example of usage:
      * ```
      * enum class E { VALUE_A, @JsonNames("ALTERNATIVE") VALUE_B }
      *
      * @Serializable
      * data class Outer(val enums: List<E>)
      *
-     * val j = Json { decodeEnumsCaseInsensitive = true }
-     * println(j.decodeFromString<Outer>("""{"enums":["value_A", "alternative"]}""").enums)
+     * val json = Json { decodeEnumsCaseInsensitive = true }
+     * // Prints [VALUE_A, VALUE_B]
+     * println(json.decodeFromString<Outer>("""{"enums":["Value_A", "alternative"]}""").enums)
+     * // Will fail with SerializationException: no such enum as 'Value_A'
+     * Json.decodeFromString<Outer>("""{"enums":["Value_A", "alternative"]}""")
      * ```
      *
-     * If this feature is enabled,
-     * it is no longer possible to decode enum values that have the same name in a lowercase form.
+     * With this feature enabled, it is no longer possible to decode enum values that have the same name in a lowercase form.
      * The following code will throw a serialization exception:
-     *
      * ```
-     * enum class BadEnum { Bad, BAD }
-     * val j = Json { decodeEnumsCaseInsensitive = true }
-     * j.decodeFromString<Box<BadEnum>>("""{"boxed":"bad"}""")
+     * enum class CaseSensitiveEnum { One, ONE }
+     * val json = Json { decodeEnumsCaseInsensitive = true }
+     * // Fails with SerializationException: The suggested name 'one' for enum value ONE is already one of the names for enum value One
+     * json.decodeFromString<CaseSensitiveEnum>("ONE")
      * ```
      */
     @ExperimentalSerializationApi
@@ -377,8 +560,7 @@
 
     /**
      * Allows parser to accept trailing (ending) commas in JSON objects and arrays,
-     * making inputs like `[1, 2, 3,]` valid.
-     *
+     * making inputs like `[1, 2, 3,]` and `{"key": "value",}` valid.
      * Does not affect encoding.
      * `false` by default.
      */
@@ -386,6 +568,57 @@
     public var allowTrailingComma: Boolean = json.configuration.allowTrailingComma
 
     /**
+     * Allows parser to accept C/Java-style comments in JSON input.
+     *
+     * Comments are being skipped and are not stored anywhere; this setting does not affect encoding in any way.
+     *
+     * More specifically, a comment is a substring that is not a part of JSON key or value, conforming to one of those:
+     *
+     * 1. Starts with `//` characters and ends with a newline character `\n`.
+     * 2. Starts with `/*` characters and ends with `*/` characters. Nesting block comments
+     *  is not supported: no matter how many `/*` characters you have, first `*/` will end the comment.
+     *
+     *  `false` by default.
+     */
+    @ExperimentalSerializationApi
+    public var allowComments: Boolean = json.configuration.allowComments
+
+    /**
+     * Removes JSON specification restriction on special floating-point values such as `NaN` and `Infinity`
+     * and enables their serialization and deserialization as float literals without quotes.
+     * When enabling it, please ensure that the receiving party will be able to encode and decode these special values.
+     * This option affects both encoding and decoding.
+     * `false` by default.
+     *
+     * Example of usage:
+     * ```
+     * val floats = listOf(1.0, 2.0, Double.NaN, Double.NEGATIVE_INFINITY)
+     * val json = Json { allowSpecialFloatingPointValues = true }
+     * // Prints [1.0,2.0,NaN,-Infinity]
+     * println(json.encodeToString(floats))
+     * // Prints [1.0, 2.0, NaN, -Infinity]
+     * println(json.decodeFromString<List<Double>>("[1.0,2.0,NaN,-Infinity]"))
+     * ```
+     */
+    public var allowSpecialFloatingPointValues: Boolean = json.configuration.allowSpecialFloatingPointValues
+
+    /**
+     * Enables structured objects to be serialized as map keys by
+     * changing serialized form of the map from JSON object (key-value pairs) to flat array like `[k1, v1, k2, v2]`.
+     * `false` by default.
+     */
+    public var allowStructuredMapKeys: Boolean = json.configuration.allowStructuredMapKeys
+
+    /**
+     * Switches polymorphic serialization to the default array format.
+     * This is an option for legacy JSON format and should not be generally used.
+     * `false` by default.
+     *
+     * This option can only be used if [classDiscriminatorMode] in a default [ClassDiscriminatorMode.POLYMORPHIC] state.
+     */
+    public var useArrayPolymorphism: Boolean = json.configuration.useArrayPolymorphism
+
+    /**
      * Module with contextual and polymorphic serializers to be used in the resulting [Json] instance.
      *
      * @see SerializersModule
@@ -422,7 +655,7 @@
             allowStructuredMapKeys, prettyPrint, explicitNulls, prettyPrintIndent,
             coerceInputValues, useArrayPolymorphism,
             classDiscriminator, allowSpecialFloatingPointValues, useAlternativeNames,
-            namingStrategy, decodeEnumsCaseInsensitive, allowTrailingComma, classDiscriminatorMode
+            namingStrategy, decodeEnumsCaseInsensitive, allowTrailingComma, allowComments, classDiscriminatorMode
         )
     }
 }
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/JsonConfiguration.kt b/formats/json/commonMain/src/kotlinx/serialization/json/JsonConfiguration.kt
index 1fa1644..ade53a6 100644
--- a/formats/json/commonMain/src/kotlinx/serialization/json/JsonConfiguration.kt
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/JsonConfiguration.kt
@@ -22,7 +22,6 @@
     public val isLenient: Boolean = false,
     public val allowStructuredMapKeys: Boolean = false,
     public val prettyPrint: Boolean = false,
-    @ExperimentalSerializationApi
     public val explicitNulls: Boolean = true,
     @ExperimentalSerializationApi
     public val prettyPrintIndent: String = "    ",
@@ -38,6 +37,13 @@
     @ExperimentalSerializationApi
     public val allowTrailingComma: Boolean = false,
     @ExperimentalSerializationApi
+    public val allowComments: Boolean = false,
+    @ExperimentalSerializationApi
+    @set:Deprecated(
+        "JsonConfiguration is not meant to be mutable, and will be made read-only in a future release. " +
+            "The `Json(from = ...) {}` copy builder should be used instead.",
+        level = DeprecationLevel.ERROR
+    )
     public var classDiscriminatorMode: ClassDiscriminatorMode = ClassDiscriminatorMode.POLYMORPHIC,
 ) {
 
@@ -49,7 +55,7 @@
                 "prettyPrintIndent='$prettyPrintIndent', coerceInputValues=$coerceInputValues, useArrayPolymorphism=$useArrayPolymorphism, " +
                 "classDiscriminator='$classDiscriminator', allowSpecialFloatingPointValues=$allowSpecialFloatingPointValues, " +
                 "useAlternativeNames=$useAlternativeNames, namingStrategy=$namingStrategy, decodeEnumsCaseInsensitive=$decodeEnumsCaseInsensitive, " +
-                "allowTrailingComma=$allowTrailingComma, classDiscriminatorMode=$classDiscriminatorMode)"
+                "allowTrailingComma=$allowTrailingComma, allowComments=$allowComments, classDiscriminatorMode=$classDiscriminatorMode)"
     }
 }
 
@@ -57,7 +63,7 @@
  * Defines which classes and objects should have their serial name included in the json as so-called class discriminator.
  *
  * Class discriminator is a JSON field added by kotlinx.serialization that has [JsonBuilder.classDiscriminator] as a key (`type` by default),
- * and class' serial name as a value (fully-qualified name by default, can be changed with [SerialName] annotation).
+ * and class' serial name as a value (fully qualified name by default, can be changed with [SerialName] annotation).
  *
  * Class discriminator is important for serializing and deserializing [polymorphic class hierarchies](https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/polymorphism.md#sealed-classes).
  * Default [ClassDiscriminatorMode.POLYMORPHIC] mode adds discriminator only to polymorphic classes.
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt b/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt
index 74abf34..47330eb 100644
--- a/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/JsonElement.kt
@@ -41,7 +41,8 @@
     public abstract val isString: Boolean
 
     /**
-     * Content of given element without quotes. For [JsonNull] this methods returns `null`
+     * Content of given element without quotes. For [JsonNull], this method returns a "null" string.
+     * [JsonPrimitive.contentOrNull] should be used for [JsonNull] to get a `null`.
      */
     public abstract val content: String
 
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/FormatLanguage.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/FormatLanguage.kt
index 275aa71..c458840 100644
--- a/formats/json/commonMain/src/kotlinx/serialization/json/internal/FormatLanguage.kt
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/FormatLanguage.kt
@@ -6,7 +6,7 @@
  * Multiplatform analogue of `org.intellij.lang.annotations.Language` annotation.
  *
  * An alias is used instead of class, because the actual class in the JVM will conflict with the class from the stdlib -
- * we want to avoid the situation with different classes having the same fully-qualified name.
+ * we want to avoid the situation with different classes having the same fully qualified name.
  * [see](https://github.com/JetBrains/java-annotations/issues/34)
  *
  * Specifies that an element of the program represents a string that is a source code on a specified language.
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonNamesMap.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonNamesMap.kt
index 9128f3a..16e6f30 100644
--- a/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonNamesMap.kt
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonNamesMap.kt
@@ -108,6 +108,20 @@
     return index
 }
 
+/**
+ * Tries to coerce value according to the rules of [JsonConfiguration.coerceInputValues] and [JsonConfiguration.explicitNulls] flags:
+ *
+ * - If a property is optional (has default), has a non-nullable type, but input was `null` literal, property is coerced. (1)
+ * - If a property is enum, but input contained string which is not a valid enum constant (3) or a `null` literal (2):
+ *   - Property is coerced in case it is optional AND non-nullable (5), or nullable AND `explicitNulls` is on (4).
+ *
+ * @param descriptor Descriptor of class that owns the property
+ * @param index The index of the element (property).
+ * @param peekNull A function to peek if the next JSON token is `null`. In case `consume` is true, should consume `null` from the input.
+ * @param peekString A function to peek the next JSON token as a string.
+ * @param onEnumCoercing A callback function to be executed when coercing an enum. Use it to discard incorrect enum constant from the input.
+ * @return `true` if value was coerced, `false` otherwise.
+ */
 @OptIn(ExperimentalSerializationApi::class)
 internal inline fun Json.tryCoerceValue(
     descriptor: SerialDescriptor,
@@ -116,18 +130,19 @@
     peekString: () -> String?,
     onEnumCoercing: () -> Unit = {}
 ): Boolean {
-    if (!descriptor.isElementOptional(index)) return false
+    val isOptional = descriptor.isElementOptional(index)
     val elementDescriptor = descriptor.getElementDescriptor(index)
-    if (!elementDescriptor.isNullable && peekNull(true)) return true
+    if (isOptional && !elementDescriptor.isNullable && peekNull(true)) return true // (1)
     if (elementDescriptor.kind == SerialKind.ENUM) {
-        if (elementDescriptor.isNullable && peekNull(false)) {
+        if (elementDescriptor.isNullable && peekNull(false)) { // (2)
             return false
         }
 
         val enumValue = peekString()
             ?: return false // if value is not a string, decodeEnum() will throw correct exception
-        val enumIndex = elementDescriptor.getJsonNameIndex(this, enumValue)
-        if (enumIndex == CompositeDecoder.UNKNOWN_NAME) {
+        val enumIndex = elementDescriptor.getJsonNameIndex(this, enumValue) // (3)
+        val coerceToNull = !configuration.explicitNulls && elementDescriptor.isNullable // (4)
+        if (enumIndex == CompositeDecoder.UNKNOWN_NAME && (isOptional || coerceToNull)) { // (3, 4, 5)
             onEnumCoercing()
             return true
         }
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonStreams.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonStreams.kt
index 05f025c..81dd4fa 100644
--- a/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonStreams.kt
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/JsonStreams.kt
@@ -14,6 +14,22 @@
     public fun write(text: String)
     public fun writeQuoted(text: String)
     public fun release()
+
+    public companion object {
+        public inline fun doWriteEscaping(text: String, writeImpl: (text: String, startIndex: Int, endIndex: Int) -> Unit) {
+            var lastPos = 0
+            for (i in text.indices) {
+                val c = text[i].code
+                if (c < ESCAPE_STRINGS.size && ESCAPE_STRINGS[c] != null) {
+                    writeImpl(text, lastPos, i) // flush prev
+                    val escape = ESCAPE_STRINGS[c]!!
+                    writeImpl(escape, 0, escape.length)
+                    lastPos = i + 1
+                }
+            }
+            writeImpl(text, lastPos, text.length)
+        }
+    }
 }
 
 @JsonFriendModuleApi
@@ -21,6 +37,58 @@
     public fun read(buffer: CharArray, bufferOffset: Int, count: Int): Int
 }
 
+// Max value for a code  point placed in one Char
+private const val SINGLE_CHAR_MAX_CODEPOINT = Char.MAX_VALUE.code
+// Value added to the high UTF-16 surrogate after shifting
+private const val HIGH_SURROGATE_HEADER = 0xd800 - (0x010000 ushr 10)
+// Value added to the low UTF-16 surrogate after masking
+private const val LOW_SURROGATE_HEADER = 0xdc00
+
+@JsonFriendModuleApi
+public abstract class InternalJsonReaderCodePointImpl: InternalJsonReader {
+    public abstract fun exhausted(): Boolean
+    public abstract fun nextCodePoint(): Int
+
+    private var bufferedChar: Char? = null
+
+    final override fun read(buffer: CharArray, bufferOffset: Int, count: Int): Int {
+        var i = 0
+
+        if (bufferedChar != null) {
+            buffer[bufferOffset + i] = bufferedChar!!
+            i++
+            bufferedChar = null
+        }
+
+        while (i < count && !exhausted()) {
+            val codePoint = nextCodePoint()
+            if (codePoint <= SINGLE_CHAR_MAX_CODEPOINT) {
+                buffer[bufferOffset + i] = codePoint.toChar()
+                i++
+            } else {
+                // an example of working with surrogates is taken from okio library with minor changes, see https://github.com/square/okio
+                // UTF-16 high surrogate: 110110xxxxxxxxxx (10 bits)
+                // UTF-16 low surrogate:  110111yyyyyyyyyy (10 bits)
+                // Unicode code point:    00010000000000000000 + xxxxxxxxxxyyyyyyyyyy (21 bits)
+                val upChar = ((codePoint ushr 10) + HIGH_SURROGATE_HEADER).toChar()
+                val lowChar = ((codePoint and 0x03ff) + LOW_SURROGATE_HEADER).toChar()
+
+                buffer[bufferOffset + i] = upChar
+                i++
+
+                if (i < count) {
+                    buffer[bufferOffset + i] = lowChar
+                    i++
+                } else {
+                    // if char array is full - buffer lower surrogate
+                    bufferedChar = lowChar
+                }
+            }
+        }
+        return if (i > 0) i else -1
+    }
+}
+
 @JsonFriendModuleApi
 public fun <T> encodeByWriter(json: Json, writer: InternalJsonWriter, serializer: SerializationStrategy<T>, value: T) {
     val encoder = StreamingJsonEncoder(
@@ -37,7 +105,7 @@
     deserializer: DeserializationStrategy<T>,
     reader: InternalJsonReader
 ): T {
-    val lexer = ReaderJsonLexer(reader)
+    val lexer = ReaderJsonLexer(json, reader)
     try {
         val input = StreamingJsonDecoder(json, WriteMode.OBJ, lexer, deserializer.descriptor, null)
         val result = input.decodeSerializableValue(deserializer)
@@ -56,7 +124,7 @@
     deserializer: DeserializationStrategy<T>,
     format: DecodeSequenceMode = DecodeSequenceMode.AUTO_DETECT
 ): Sequence<T> {
-    val lexer = ReaderJsonLexer(reader, CharArray(BATCH_SIZE)) // Unpooled buffer due to lazy nature of sequence
+    val lexer = ReaderJsonLexer(json, reader, CharArray(BATCH_SIZE)) // Unpooled buffer due to lazy nature of sequence
     val iter = JsonIterator(format, json, lexer, deserializer)
     return Sequence { iter }.constrainOnce()
 }
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/Polymorphic.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/Polymorphic.kt
index 636f340..acc0bf4 100644
--- a/formats/json/commonMain/src/kotlinx/serialization/json/internal/Polymorphic.kt
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/Polymorphic.kt
@@ -16,7 +16,7 @@
 internal inline fun <T> JsonEncoder.encodePolymorphically(
     serializer: SerializationStrategy<T>,
     value: T,
-    ifPolymorphic: (String) -> Unit
+    ifPolymorphic: (discriminatorName: String, serialName: String) -> Unit
 ) {
     if (json.configuration.useArrayPolymorphism) {
         serializer.serialize(this, value)
@@ -42,7 +42,7 @@
         actual as SerializationStrategy<T>
     } else serializer
 
-    if (baseClassDiscriminator != null) ifPolymorphic(baseClassDiscriminator)
+    if (baseClassDiscriminator != null) ifPolymorphic(baseClassDiscriminator, actualSerializer.descriptor.serialName)
     actualSerializer.serialize(this, value)
 }
 
@@ -71,14 +71,14 @@
     if (kind is PolymorphicKind) error("Actual serializer for polymorphic cannot be polymorphic itself")
 }
 
-internal fun <T> JsonDecoder.decodeSerializableValuePolymorphic(deserializer: DeserializationStrategy<T>): T {
+internal inline fun <T> JsonDecoder.decodeSerializableValuePolymorphic(deserializer: DeserializationStrategy<T>, path: () -> String): T {
     // NB: changes in this method should be reflected in StreamingJsonDecoder#decodeSerializableValue
     if (deserializer !is AbstractPolymorphicSerializer<*> || json.configuration.useArrayPolymorphism) {
         return deserializer.deserialize(this)
     }
     val discriminator = deserializer.descriptor.classDiscriminator(json)
 
-    val jsonTree = cast<JsonObject>(decodeJsonElement(), deserializer.descriptor)
+    val jsonTree = cast<JsonObject>(decodeJsonElement(), deserializer.descriptor.serialName, path)
     val type = jsonTree[discriminator]?.jsonPrimitive?.contentOrNull // differentiate between `"type":"null"` and `"type":null`.
     @Suppress("UNCHECKED_CAST")
     val actualSerializer =
@@ -100,3 +100,7 @@
     return json.configuration.classDiscriminator
 }
 
+internal fun throwJsonElementPolymorphicException(serialName: String?, element: JsonElement): Nothing {
+    throw JsonEncodingException("Class with serial name $serialName cannot be serialized polymorphically because it is represented as ${element::class.simpleName}. Make sure that its JsonTransformingSerializer returns JsonObject, so class discriminator can be added to it.")
+}
+
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/StreamingJsonDecoder.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/StreamingJsonDecoder.kt
index caa1f4a..ee813b3 100644
--- a/formats/json/commonMain/src/kotlinx/serialization/json/internal/StreamingJsonDecoder.kt
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/StreamingJsonDecoder.kt
@@ -72,7 +72,7 @@
             val discriminator = deserializer.descriptor.classDiscriminator(json)
             val type = lexer.peekLeadingMatchingValue(discriminator, configuration.isLenient)
                 ?: // Fallback to slow path if we haven't found discriminator on first try
-                return decodeSerializableValuePolymorphic<T>(deserializer as DeserializationStrategy<T>)
+                return decodeSerializableValuePolymorphic<T>(deserializer as DeserializationStrategy<T>) { lexer.path.getPath() }
 
             @Suppress("UNCHECKED_CAST")
             val actualSerializer = try {
@@ -359,7 +359,7 @@
     deserializer: DeserializationStrategy<T>,
     source: String
 ): JsonElement {
-    val lexer = StringJsonLexer(source)
+    val lexer = StringJsonLexer(json, source)
     val input = StreamingJsonDecoder(json, WriteMode.OBJ, lexer, deserializer.descriptor, null)
     val tree = input.decodeJsonElement()
     lexer.expectEof()
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/StreamingJsonEncoder.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/StreamingJsonEncoder.kt
index cf562de..4eaf079 100644
--- a/formats/json/commonMain/src/kotlinx/serialization/json/internal/StreamingJsonEncoder.kt
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/StreamingJsonEncoder.kt
@@ -43,6 +43,7 @@
     // Forces serializer to wrap all values into quotes
     private var forceQuoting: Boolean = false
     private var polymorphicDiscriminator: String? = null
+    private var polymorphicSerialName: String? = null
 
     init {
         val i = mode.ordinal
@@ -53,6 +54,9 @@
     }
 
     override fun encodeJsonElement(element: JsonElement) {
+        if (polymorphicDiscriminator != null && element !is JsonObject) {
+            throwJsonElementPolymorphicException(polymorphicSerialName, element)
+        }
         encodeSerializableValue(JsonElementSerializer, element)
     }
 
@@ -61,17 +65,18 @@
     }
 
     override fun <T> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) {
-        encodePolymorphically(serializer, value) {
-            polymorphicDiscriminator = it
+        encodePolymorphically(serializer, value) { discriminatorName, serialName ->
+            polymorphicDiscriminator = discriminatorName
+            polymorphicSerialName = serialName
         }
     }
 
-    private fun encodeTypeInfo(descriptor: SerialDescriptor) {
+    private fun encodeTypeInfo(discriminator: String, serialName: String) {
         composer.nextItem()
-        encodeString(polymorphicDiscriminator!!)
+        encodeString(discriminator)
         composer.print(COLON)
         composer.space()
-        encodeString(descriptor.serialName)
+        encodeString(serialName)
     }
 
     override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder {
@@ -81,9 +86,11 @@
             composer.indent()
         }
 
-        if (polymorphicDiscriminator != null) {
-            encodeTypeInfo(descriptor)
+        val discriminator = polymorphicDiscriminator
+        if (discriminator != null) {
+            encodeTypeInfo(discriminator, polymorphicSerialName ?: descriptor.serialName)
             polymorphicDiscriminator = null
+            polymorphicSerialName = null
         }
 
         if (mode == newMode) {
@@ -160,6 +167,7 @@
         when {
             descriptor.isUnsignedNumber -> StreamingJsonEncoder(composerAs(::ComposerForUnsignedNumbers), json, mode, null)
             descriptor.isUnquotedLiteral -> StreamingJsonEncoder(composerAs(::ComposerForUnquotedLiterals), json, mode, null)
+            polymorphicDiscriminator != null -> apply { polymorphicSerialName = descriptor.serialName }
             else                        -> super.encodeInline(descriptor)
         }
 
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/StringOps.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/StringOps.kt
index ed76ba0..1f36715 100644
--- a/formats/json/commonMain/src/kotlinx/serialization/json/internal/StringOps.kt
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/StringOps.kt
@@ -12,6 +12,7 @@
     else (d - 10 + 'a'.code).toChar()
 }
 
+@PublishedApi
 internal val ESCAPE_STRINGS: Array<String?> = arrayOfNulls<String>(93).apply {
     for (c in 0..0x1f) {
         val c1 = toHexChar(c shr 12)
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/TreeJsonDecoder.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/TreeJsonDecoder.kt
index 690b35e..ec06db6 100644
--- a/formats/json/commonMain/src/kotlinx/serialization/json/internal/TreeJsonDecoder.kt
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/TreeJsonDecoder.kt
@@ -8,6 +8,7 @@
 package kotlinx.serialization.json.internal
 
 import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
 import kotlinx.serialization.descriptors.*
 import kotlinx.serialization.encoding.*
 import kotlinx.serialization.internal.*
@@ -35,7 +36,8 @@
 
 private sealed class AbstractJsonTreeDecoder(
     override val json: Json,
-    open val value: JsonElement
+    open val value: JsonElement,
+    protected val polymorphicDiscriminator: String? = null
 ) : NamedValueDecoder(), JsonDecoder {
 
     override val serializersModule: SerializersModule
@@ -46,10 +48,12 @@
 
     protected fun currentObject() = currentTagOrNull?.let { currentElement(it) } ?: value
 
+    fun renderTagStack(currentTag: String) = renderTagStack() + ".$currentTag"
+
     override fun decodeJsonElement(): JsonElement = currentObject()
 
     override fun <T> decodeSerializableValue(deserializer: DeserializationStrategy<T>): T {
-        return decodeSerializableValuePolymorphic(deserializer)
+        return decodeSerializableValuePolymorphic(deserializer, ::renderTagStack)
     }
 
     override fun composeName(parentName: String, childName: String): String = childName
@@ -63,107 +67,111 @@
                 { JsonTreeMapDecoder(json, cast(currentObject, descriptor)) },
                 { JsonTreeListDecoder(json, cast(currentObject, descriptor)) }
             )
-            else -> JsonTreeDecoder(json, cast(currentObject, descriptor))
+            else -> JsonTreeDecoder(json, cast(currentObject, descriptor), polymorphicDiscriminator)
         }
     }
 
+    inline fun <reified T : JsonElement> cast(value: JsonElement, descriptor: SerialDescriptor): T = cast(value, descriptor.serialName) { renderTagStack() }
+    inline fun <reified T : JsonElement> cast(value: JsonElement, serialName: String, tag: String): T = cast(value, serialName) { renderTagStack(tag) }
+
     override fun endStructure(descriptor: SerialDescriptor) {
         // Nothing
     }
 
     override fun decodeNotNullMark(): Boolean = currentObject() !is JsonNull
 
-    protected fun getPrimitiveValue(tag: String): JsonPrimitive {
-        val currentElement = currentElement(tag)
-        return currentElement as? JsonPrimitive ?: throw JsonDecodingException(
-            -1,
-            "Expected JsonPrimitive at $tag, found $currentElement", currentObject().toString()
-        )
+    @Suppress("NOTHING_TO_INLINE")
+    protected inline fun getPrimitiveValue(tag: String, descriptor: SerialDescriptor): JsonPrimitive =
+        cast(currentElement(tag), descriptor.serialName, tag)
+
+    private inline fun <T : Any> getPrimitiveValue(tag: String, primitiveName: String, convert: JsonPrimitive.() -> T?): T {
+        val literal = cast<JsonPrimitive>(currentElement(tag), primitiveName, tag)
+        try {
+            return literal.convert() ?: unparsedPrimitive(literal, primitiveName, tag)
+        } catch (e: IllegalArgumentException) {
+            // TODO: pass e as cause? (may conflict with #2590)
+            unparsedPrimitive(literal, primitiveName, tag)
+        }
+    }
+
+    private fun unparsedPrimitive(literal: JsonPrimitive, primitive: String, tag: String): Nothing {
+        val type = if (primitive.startsWith("i")) "an $primitive" else "a $primitive"
+        throw JsonDecodingException(-1, "Failed to parse literal '$literal' as $type value at element: ${renderTagStack(tag)}", currentObject().toString())
     }
 
     protected abstract fun currentElement(tag: String): JsonElement
 
     override fun decodeTaggedEnum(tag: String, enumDescriptor: SerialDescriptor): Int =
-        enumDescriptor.getJsonNameIndexOrThrow(json, getPrimitiveValue(tag).content)
+        enumDescriptor.getJsonNameIndexOrThrow(json, getPrimitiveValue(tag, enumDescriptor).content)
 
     override fun decodeTaggedNull(tag: String): Nothing? = null
 
     override fun decodeTaggedNotNullMark(tag: String): Boolean = currentElement(tag) !== JsonNull
 
-    override fun decodeTaggedBoolean(tag: String): Boolean {
-        return getPrimitiveValue(tag).primitive("boolean", JsonPrimitive::booleanOrNull)
-    }
+    override fun decodeTaggedBoolean(tag: String): Boolean =
+        getPrimitiveValue(tag, "boolean", JsonPrimitive::booleanOrNull)
 
-    override fun decodeTaggedByte(tag: String) = getPrimitiveValue(tag).primitive("byte") {
+    override fun decodeTaggedByte(tag: String) = getPrimitiveValue(tag, "byte") {
         val result = int
         if (result in Byte.MIN_VALUE..Byte.MAX_VALUE) result.toByte()
         else null
     }
 
-    override fun decodeTaggedShort(tag: String) = getPrimitiveValue(tag).primitive("short") {
+    override fun decodeTaggedShort(tag: String) = getPrimitiveValue(tag, "short") {
         val result = int
         if (result in Short.MIN_VALUE..Short.MAX_VALUE) result.toShort()
         else null
     }
 
-    override fun decodeTaggedInt(tag: String) = getPrimitiveValue(tag).primitive("int") { int }
-    override fun decodeTaggedLong(tag: String) = getPrimitiveValue(tag).primitive("long") { long }
+    override fun decodeTaggedInt(tag: String) = getPrimitiveValue(tag, "int") { int }
+    override fun decodeTaggedLong(tag: String) = getPrimitiveValue(tag, "long") { long }
 
     override fun decodeTaggedFloat(tag: String): Float {
-        val result = getPrimitiveValue(tag).primitive("float") { float }
+        val result = getPrimitiveValue(tag, "float") { float }
         val specialFp = json.configuration.allowSpecialFloatingPointValues
         if (specialFp || result.isFinite()) return result
         throw InvalidFloatingPointDecoded(result, tag, currentObject().toString())
     }
 
     override fun decodeTaggedDouble(tag: String): Double {
-        val result = getPrimitiveValue(tag).primitive("double") { double }
+        val result = getPrimitiveValue(tag, "double") { double }
         val specialFp = json.configuration.allowSpecialFloatingPointValues
         if (specialFp || result.isFinite()) return result
         throw InvalidFloatingPointDecoded(result, tag, currentObject().toString())
     }
 
-    override fun decodeTaggedChar(tag: String): Char = getPrimitiveValue(tag).primitive("char") { content.single() }
-
-    private inline fun <T : Any> JsonPrimitive.primitive(primitive: String, block: JsonPrimitive.() -> T?): T {
-        try {
-            return block() ?: unparsedPrimitive(primitive)
-        } catch (e: IllegalArgumentException) {
-            unparsedPrimitive(primitive)
-        }
-    }
-
-    private fun unparsedPrimitive(primitive: String): Nothing {
-        throw JsonDecodingException(-1, "Failed to parse literal as '$primitive' value", currentObject().toString())
-    }
+    override fun decodeTaggedChar(tag: String): Char = getPrimitiveValue(tag, "char") { content.single() }
 
     override fun decodeTaggedString(tag: String): String {
-        val value = getPrimitiveValue(tag)
-        if (!json.configuration.isLenient) {
-            val literal = value.asLiteral("string")
-            if (!literal.isString) throw JsonDecodingException(
-                -1, "String literal for key '$tag' should be quoted.\n$lenientHint", currentObject().toString()
+        val value = cast<JsonPrimitive>(currentElement(tag), "string", tag)
+        if (value !is JsonLiteral)
+            throw JsonDecodingException(-1, "Expected string value for a non-null key '$tag', got null literal instead at element: ${renderTagStack(tag)}", currentObject().toString())
+        if (!value.isString && !json.configuration.isLenient) {
+            throw JsonDecodingException(
+                -1, "String literal for key '$tag' should be quoted at element: ${renderTagStack(tag)}.\n$lenientHint", currentObject().toString()
             )
         }
-        if (value is JsonNull) throw JsonDecodingException(-1, "Unexpected 'null' value instead of string literal", currentObject().toString())
         return value.content
     }
 
-    private fun JsonPrimitive.asLiteral(type: String): JsonLiteral {
-        return this as? JsonLiteral ?: throw JsonDecodingException(-1, "Unexpected 'null' literal when non-nullable $type was expected")
+    override fun decodeTaggedInline(tag: String, inlineDescriptor: SerialDescriptor): Decoder {
+        return if (inlineDescriptor.isUnsignedNumber) {
+            val lexer = StringJsonLexer(json, getPrimitiveValue(tag, inlineDescriptor).content)
+            JsonDecoderForUnsignedTypes(lexer, json)
+        } else super.decodeTaggedInline(tag, inlineDescriptor)
     }
 
-    override fun decodeTaggedInline(tag: String, inlineDescriptor: SerialDescriptor): Decoder =
-        if (inlineDescriptor.isUnsignedNumber) JsonDecoderForUnsignedTypes(StringJsonLexer(getPrimitiveValue(tag).content), json)
-        else super.decodeTaggedInline(tag, inlineDescriptor)
-
     override fun decodeInline(descriptor: SerialDescriptor): Decoder {
         return if (currentTagOrNull != null) super.decodeInline(descriptor)
-        else JsonPrimitiveDecoder(json, value).decodeInline(descriptor)
+        else JsonPrimitiveDecoder(json, value, polymorphicDiscriminator).decodeInline(descriptor)
     }
 }
 
-private class JsonPrimitiveDecoder(json: Json, override val value: JsonElement) : AbstractJsonTreeDecoder(json, value) {
+private class JsonPrimitiveDecoder(
+    json: Json,
+    override val value: JsonElement,
+    polymorphicDiscriminator: String? = null
+) : AbstractJsonTreeDecoder(json, value, polymorphicDiscriminator) {
 
     init {
         pushTag(PRIMITIVE_TAG)
@@ -180,9 +188,9 @@
 private open class JsonTreeDecoder(
     json: Json,
     override val value: JsonObject,
-    private val polyDiscriminator: String? = null,
+    polymorphicDiscriminator: String? = null,
     private val polyDescriptor: SerialDescriptor? = null
-) : AbstractJsonTreeDecoder(json, value) {
+) : AbstractJsonTreeDecoder(json, value, polymorphicDiscriminator) {
     private var position = 0
     private var forceNull: Boolean = false
     /*
@@ -251,7 +259,7 @@
         // in endStructure can filter polyDiscriminator out.
         if (descriptor === polyDescriptor) {
             return JsonTreeDecoder(
-                json, cast(currentObject(), polyDescriptor), polyDiscriminator, polyDescriptor
+                json, cast(currentObject(), polyDescriptor), polymorphicDiscriminator, polyDescriptor
             )
         }
 
@@ -271,7 +279,7 @@
         }
 
         for (key in value.keys) {
-            if (key !in names && key != polyDiscriminator) {
+            if (key !in names && key != polymorphicDiscriminator) {
                 throw UnknownKeyException(key, value.toString())
             }
         }
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/TreeJsonEncoder.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/TreeJsonEncoder.kt
index 5e3c808..74c95b1 100644
--- a/formats/json/commonMain/src/kotlinx/serialization/json/internal/TreeJsonEncoder.kt
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/TreeJsonEncoder.kt
@@ -35,11 +35,15 @@
     protected val configuration = json.configuration
 
     private var polymorphicDiscriminator: String? = null
+    private var polymorphicSerialName: String? = null
 
     override fun elementName(descriptor: SerialDescriptor, index: Int): String =
         descriptor.getJsonElementName(json, index)
 
     override fun encodeJsonElement(element: JsonElement) {
+        if (polymorphicDiscriminator != null && element !is JsonObject) {
+            throwJsonElementPolymorphicException(polymorphicSerialName, element)
+        }
         encodeSerializableValue(JsonElementSerializer, element)
     }
 
@@ -77,7 +81,10 @@
     override fun <T> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) {
         // Writing non-structured data (i.e. primitives) on top-level (e.g. without any tag) requires special output
         if (currentTagOrNull != null || !serializer.descriptor.carrierDescriptor(serializersModule).requiresTopLevelTag) {
-            encodePolymorphically(serializer, value) { polymorphicDiscriminator = it }
+            encodePolymorphically(serializer, value) { discriminatorName, serialName ->
+                polymorphicDiscriminator = discriminatorName
+                polymorphicSerialName = serialName
+            }
         } else JsonPrimitiveEncoder(json, nodeConsumer).apply {
             encodeSerializableValue(serializer, value)
         }
@@ -112,8 +119,12 @@
         }
 
     override fun encodeInline(descriptor: SerialDescriptor): Encoder {
-        return if (currentTagOrNull != null) super.encodeInline(descriptor)
-        else JsonPrimitiveEncoder(json, nodeConsumer).encodeInline(descriptor)
+        return if (currentTagOrNull != null) {
+            if (polymorphicDiscriminator != null) polymorphicSerialName = descriptor.serialName
+            super.encodeInline(descriptor)
+        } else {
+            JsonPrimitiveEncoder(json, nodeConsumer).encodeInline(descriptor)
+        }
     }
 
     @SuppressAnimalSniffer // Long(Integer).toUnsignedString(long)
@@ -148,9 +159,18 @@
             else -> JsonTreeEncoder(json, consumer)
         }
 
-        if (polymorphicDiscriminator != null) {
-            encoder.putElement(polymorphicDiscriminator!!, JsonPrimitive(descriptor.serialName))
+        val discriminator = polymorphicDiscriminator
+        if (discriminator != null) {
+            if (encoder is JsonTreeMapEncoder) {
+                // first parameter of `putElement` is ignored in JsonTreeMapEncoder
+                encoder.putElement("key", JsonPrimitive(discriminator))
+                encoder.putElement("value", JsonPrimitive(polymorphicSerialName ?: descriptor.serialName))
+
+            } else {
+                encoder.putElement(discriminator, JsonPrimitive(polymorphicSerialName ?: descriptor.serialName))
+            }
             polymorphicDiscriminator = null
+            polymorphicSerialName = null
         }
 
         return encoder
@@ -247,12 +267,12 @@
     override fun getCurrent(): JsonElement = JsonArray(array)
 }
 
-@OptIn(ExperimentalSerializationApi::class)
-internal inline fun <reified T : JsonElement> cast(value: JsonElement, descriptor: SerialDescriptor): T {
+internal inline fun <reified T : JsonElement> cast(value: JsonElement, serialName: String, path: () -> String): T {
     if (value !is T) {
         throw JsonDecodingException(
             -1,
-            "Expected ${T::class} as the serialized body of ${descriptor.serialName}, but had ${value::class}"
+            "Expected ${T::class.simpleName}, but had ${value::class.simpleName} as the serialized body of $serialName at element: ${path()}",
+            value.toString()
         )
     }
     return value
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/AbstractJsonLexer.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/AbstractJsonLexer.kt
index f90ee1a..7b74008 100644
--- a/formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/AbstractJsonLexer.kt
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/AbstractJsonLexer.kt
@@ -154,6 +154,9 @@
     @JvmField
     val path = JsonPath()
 
+    @Suppress("NOTHING_TO_INLINE")
+    protected inline fun Char.isWs() = this == ' ' || this == '\n' || this == '\r' || this == '\t'
+
     open fun ensureHaveChars() {}
 
     fun isNotEof(): Boolean = peekNextToken() != TC_EOF
@@ -161,12 +164,21 @@
     // Used as bound check in loops
     abstract fun prefetchOrEof(position: Int): Int
 
-    abstract fun tryConsumeComma(): Boolean
-
     abstract fun canConsumeValue(): Boolean
 
     abstract fun consumeNextToken(): Byte
 
+    fun tryConsumeComma(): Boolean {
+        val current = skipWhitespaces()
+        val source = source
+        if (current >= source.length || current == -1) return false
+        if (source[current] == ',') {
+            ++currentPosition
+            return true
+        }
+        return false
+    }
+
     protected fun isValidValueStart(c: Char): Boolean {
         return when (c) {
             '}', ']', ':', ',' -> false
@@ -196,22 +208,8 @@
         return token
     }
 
-    open fun consumeNextToken(expected: Char) {
-        ensureHaveChars()
-        val source = source
-        var cpos = currentPosition
-        while (true) {
-            cpos = prefetchOrEof(cpos)
-            if (cpos == -1) break // could be inline function but KT-1436
-            val c = source[cpos++]
-            if (c == ' ' || c == '\n' || c == '\r' || c == '\t') continue
-            currentPosition = cpos
-            if (c == expected) return
-            unexpectedToken(expected)
-        }
-        currentPosition = cpos
-        unexpectedToken(expected) // EOF
-    }
+
+    abstract fun consumeNextToken(expected: Char)
 
     protected fun unexpectedToken(expected: Char) {
         if (currentPosition > 0 && expected == STRING) {
@@ -233,7 +231,7 @@
         fail("Expected $expected, but had '$s' instead", position)
     }
 
-    fun peekNextToken(): Byte {
+    open fun peekNextToken(): Byte {
         val source = source
         var cpos = currentPosition
         while (true) {
@@ -277,23 +275,7 @@
         return true
     }
 
-    open fun skipWhitespaces(): Int {
-        var current = currentPosition
-        // Skip whitespaces
-        while (true) {
-            current = prefetchOrEof(current)
-            if (current == -1) break
-            val c = source[current]
-            // Faster than char2TokenClass actually
-            if (c == ' ' || c == '\n' || c == '\r' || c == '\t') {
-                ++current
-            } else {
-                break
-            }
-        }
-        currentPosition = current
-        return current
-    }
+    abstract fun skipWhitespaces(): Int
 
     abstract fun peekLeadingMatchingValue(keyToMatch: String, isLenient: Boolean): String?
 
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/CommentLexers.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/CommentLexers.kt
new file mode 100644
index 0000000..8d6ca65
--- /dev/null
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/CommentLexers.kt
@@ -0,0 +1,218 @@
+/*
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.json.internal
+
+/*
+ Implementations of these two classes are nearly identical. However, there are several reasons why it can't be unified
+ and merged into one lexer:
+
+ 1. Making skipWhitespaces() a separate method slows deserialization down for about 10-15%. It is faster to have
+ `if (c == ' ' || c == '\n' || c == '\r' || c == '\t')` handling copy-pasted instead of separate method; however, copy-pasting comment
+ handling will make the code too big and hardly possible to maintain. Therefore, implementation without comment support should not be delegated to skipWhitespaces().
+
+ 2. We assume that most users do not need comment support and therefore the only implementation of lexer will be StringJsonLexer.
+ JIT therefore will be able to devirtualize and inline calls well; however, if there is more than one class loaded, it will
+ be harder to predict if performance is stable. Consequently, StringJsonLexer should inherit AbstractJsonLexer directly and we can't move
+ generalized implementation to some CommentJsonLexer in between.
+ */
+
+internal class StringJsonLexerWithComments(source: String): StringJsonLexer(source) {
+    override fun consumeNextToken(): Byte {
+        val source = source
+        val cpos = skipWhitespaces()
+        if (cpos >= source.length || cpos == -1) return TC_EOF
+        currentPosition = cpos + 1
+        return charToTokenClass(source[cpos])
+    }
+
+    override fun canConsumeValue(): Boolean {
+        val current = skipWhitespaces()
+        if (current >= source.length || current == -1) return false
+        return isValidValueStart(source[current])
+    }
+
+    override fun consumeNextToken(expected: Char) {
+        val source = source
+        val current = skipWhitespaces()
+        if (current >= source.length || current == -1) {
+            currentPosition = -1 // for correct EOF reporting
+            unexpectedToken(expected) // EOF
+        }
+        val c = source[current]
+        currentPosition = current + 1
+        if (c == expected) return
+        else unexpectedToken(expected)
+    }
+
+    override fun peekNextToken(): Byte {
+        val source = source
+        val cpos = skipWhitespaces()
+        // skipWhitespaces() calls prefetch() on every iteration, so we can be sure that there's at least THRESHOLD-1 chars in buf when it returns.
+        if (cpos >= source.length || cpos == -1) return TC_EOF
+        currentPosition = cpos // only difference with consumeNextToken(), actually
+        return charToTokenClass(source[cpos])
+    }
+
+    override fun skipWhitespaces(): Int {
+        var current = currentPosition
+        if (current == -1) return current
+        val source = source
+        // Skip whitespaces
+        while (current < source.length) {
+            val c = source[current]
+            // Faster than char2TokenClass actually
+            if (c.isWs()) {
+                ++current
+                continue
+            }
+            if (c == '/' && current + 1 < source.length) { // potential comment start
+                when(source[current + 1]) {
+                    '/' -> {
+                        current = source.indexOf('\n', current + 2)
+                        if (current == -1) current = source.length else current++ // skip char itself
+                        continue
+                    }
+                    '*' -> {
+                        current = source.indexOf("*/", current + 2)
+                        if (current == -1) {
+                            currentPosition = source.length
+                            fail("Expected end of the block comment: \"*/\", but had EOF instead")
+                        } else {
+                            current += 2 // skip */ chars
+                        }
+                        continue
+                    }
+                }
+            }
+            break
+        }
+        currentPosition = current
+        return current
+    }
+}
+
+internal class ReaderJsonLexerWithComments(reader: InternalJsonReader, buffer: CharArray): ReaderJsonLexer(reader, buffer) {
+    override fun consumeNextToken(expected: Char) {
+        ensureHaveChars()
+        val source = source
+        val current = skipWhitespaces()
+        // skipWhitespaces() calls prefetch() on every iteration, so we can be sure that there's at least THRESHOLD-1 chars in buf when it returns.
+        if (current >= source.length || current == -1) {
+            currentPosition = -1 // for correct EOF reporting
+            unexpectedToken(expected) // EOF
+        }
+        val c = source[current]
+        currentPosition = current + 1
+        if (c == expected) return
+        else unexpectedToken(expected)
+    }
+
+    override fun canConsumeValue(): Boolean {
+        ensureHaveChars()
+        val current = skipWhitespaces()
+        // skipWhitespaces() calls prefetch() on every iteration, so we can be sure that there's at least THRESHOLD-1 chars in buf when it returns.
+        if (current >= source.length || current == -1) return false
+        return isValidValueStart(source[current])
+    }
+
+    override fun consumeNextToken(): Byte {
+        ensureHaveChars()
+        val source = source
+        val cpos = skipWhitespaces()
+        if (cpos >= source.length || cpos == -1) return TC_EOF
+        currentPosition = cpos + 1
+        return charToTokenClass(source[cpos])
+    }
+
+    override fun peekNextToken(): Byte {
+        ensureHaveChars()
+        val source = source
+        val cpos = skipWhitespaces()
+        // skipWhitespaces() calls prefetch() on every iteration, so we can be sure that there's at least THRESHOLD-1 chars in buf when it returns.
+        if (cpos >= source.length || cpos == -1) return TC_EOF
+        currentPosition = cpos // only difference with consumeNextToken(), actually
+        return charToTokenClass(source[cpos])
+    }
+
+    private fun handleComment(position: Int): Pair<Int, Boolean> {
+        var current = position
+        var startIndex = current + 2
+        when (source[current + 1]) {
+            '/' -> {
+                while(current != -1) {
+                    current = source.indexOf('\n', startIndex)
+                    if (current == -1) {
+                        current = prefetchOrEof(source.length)
+                        startIndex = current
+                    } else {
+                        return current + 1 to true
+                    }
+                }
+                // reached end of stream.
+                return -1 to true
+            }
+
+            '*' -> {
+                var rareCaseHit = false
+                while (current != -1) {
+                    current = source.indexOf("*/", startIndex)
+                    if (current != -1) {
+                        return current + 2 to true
+                    } else if (source[source.length - 1] != '*') {
+                        current = prefetchOrEof(source.length)
+                        startIndex = current
+                    } else {
+                        // Rare case: */ got split by batch boundary (see JsonCommentsTest.testCommentsOnThresholdEdge)
+                        // In this case, we should manually force next batch loading with 1 char left in buffer
+                        current = prefetchWithinThreshold(source.length - 1)
+                        // However, we also can stuck in a situation where comment is unclosed
+                        // and * without / is a last char in the buffer. So to avoid checking it in infinite cycle,
+                        // there's an escape hatch:
+                        if (rareCaseHit) {
+                            break
+                        }
+                        rareCaseHit = true
+                        startIndex = current
+                    }
+                }
+                // reached end of stream.
+                currentPosition = source.length
+                fail("Expected end of the block comment: \"*/\", but had EOF instead")
+            }
+        }
+        return current to false
+    }
+
+    private fun prefetchWithinThreshold(position: Int): Int {
+        if (source.length - position > threshold) return position
+        currentPosition = position
+        ensureHaveChars()
+        if (currentPosition != 0 || source.isEmpty()) return -1 // if something was loaded, then it would be zero.
+        return 0
+    }
+
+    override fun skipWhitespaces(): Int {
+        var current = currentPosition
+        // Skip whitespaces
+        while (true) {
+            current = prefetchOrEof(current)
+            if (current == -1) break
+            val c = source[current]
+            // Faster than char2TokenClass actually
+            if (c.isWs()) {
+                ++current
+                continue
+            }
+            if (c == '/' && current + 1 < source.length) { // potential comment start
+                val (new, cont) = handleComment(current)
+                current = new
+                if (cont) continue
+            }
+            break
+        }
+        currentPosition = current
+        return current
+    }
+}
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/ReaderJsonLexer.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/ReaderJsonLexer.kt
index 24e5b47..81c9758 100644
--- a/formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/ReaderJsonLexer.kt
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/ReaderJsonLexer.kt
@@ -4,6 +4,11 @@
 
 package kotlinx.serialization.json.internal
 
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlin.jvm.*
+import kotlin.math.*
+
 internal const val BATCH_SIZE: Int = 16 * 1024
 private const val DEFAULT_THRESHOLD = 128
 
@@ -32,11 +37,17 @@
     override fun toString(): String = substring(0, length)
 }
 
-internal class ReaderJsonLexer(
-    private val reader: InternalJsonReader,
-    private val buffer: CharArray = CharArrayPoolBatchSize.take()
+@OptIn(ExperimentalSerializationApi::class)
+internal fun ReaderJsonLexer(json: Json, reader: InternalJsonReader, buffer: CharArray = CharArrayPoolBatchSize.take()) =
+    if (!json.configuration.allowComments) ReaderJsonLexer(reader, buffer) else ReaderJsonLexerWithComments(reader, buffer)
+
+internal open class ReaderJsonLexer(
+    val reader: InternalJsonReader,
+    val buffer: CharArray = CharArrayPoolBatchSize.take()
 ) : AbstractJsonLexer() {
-    private var threshold: Int = DEFAULT_THRESHOLD // chars
+
+    @JvmField
+    protected var threshold: Int = DEFAULT_THRESHOLD // chars
 
     override val source: ArrayAsSequence = ArrayAsSequence(buffer)
 
@@ -44,16 +55,6 @@
         preload(0)
     }
 
-    override fun tryConsumeComma(): Boolean {
-        val current = skipWhitespaces()
-        if (current >= source.length || current == -1) return false
-        if (source[current] == ',') {
-            ++currentPosition
-            return true
-        }
-        return false
-    }
-
     override fun canConsumeValue(): Boolean {
         ensureHaveChars()
         var current = currentPosition
@@ -62,7 +63,7 @@
             if (current == -1) break // could be inline function but KT-1436
             val c = source[current]
             // Inlined skipWhitespaces without field spill and nested loop. Also faster then char2TokenClass
-            if (c == ' ' || c == '\n' || c == '\r' || c == '\t') {
+            if (c.isWs()) {
                 ++current
                 continue
             }
@@ -121,6 +122,41 @@
         return TC_EOF
     }
 
+    override fun consumeNextToken(expected: Char) {
+        ensureHaveChars()
+        val source = source
+        var cpos = currentPosition
+        while (true) {
+            cpos = prefetchOrEof(cpos)
+            if (cpos == -1) break // could be inline function but KT-1436
+            val c = source[cpos++]
+            if (c.isWs()) continue
+            currentPosition = cpos
+            if (c == expected) return
+            unexpectedToken(expected)
+        }
+        currentPosition = cpos
+        unexpectedToken(expected) // EOF
+    }
+
+    override fun skipWhitespaces(): Int {
+        var current = currentPosition
+        // Skip whitespaces
+        while (true) {
+            current = prefetchOrEof(current)
+            if (current == -1) break
+            val c = source[current]
+            // Faster than char2TokenClass actually
+            if (c.isWs()) {
+                ++current
+            } else {
+                break
+            }
+        }
+        currentPosition = current
+        return current
+    }
+
     override fun ensureHaveChars() {
         val cur = currentPosition
         val oldSize = source.length
diff --git a/formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/StringJsonLexer.kt b/formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/StringJsonLexer.kt
index 9f2e519..727b499 100644
--- a/formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/StringJsonLexer.kt
+++ b/formats/json/commonMain/src/kotlinx/serialization/json/internal/lexer/StringJsonLexer.kt
@@ -4,39 +4,38 @@
 
 package kotlinx.serialization.json.internal
 
-internal class StringJsonLexer(override val source: String) : AbstractJsonLexer() {
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@OptIn(ExperimentalSerializationApi::class)
+internal fun StringJsonLexer(json: Json, source: String) = if (!json.configuration.allowComments) StringJsonLexer(source) else StringJsonLexerWithComments(source)
+
+@Suppress("unused")
+internal open class StringJsonLexer(override val source: String) : AbstractJsonLexer() {
 
     override fun prefetchOrEof(position: Int): Int = if (position < source.length) position else -1
 
     override fun consumeNextToken(): Byte {
         val source = source
-        while (currentPosition != -1 && currentPosition < source.length) {
-            val ch = source[currentPosition++]
-            return when (val tc = charToTokenClass(ch)) {
-                TC_WHITESPACE -> continue
-                else -> tc
-            }
+        var cpos = currentPosition
+        while (cpos != -1 && cpos < source.length) {
+            val c = source[cpos++]
+            if (c.isWs()) continue
+            currentPosition = cpos
+            return charToTokenClass(c)
         }
+        currentPosition = source.length
         return TC_EOF
     }
 
-    override fun tryConsumeComma(): Boolean {
-        val current = skipWhitespaces()
-        if (current == source.length || current == -1) return false
-        if (source[current] == ',') {
-            ++currentPosition
-            return true
-        }
-        return false
-    }
-
     override fun canConsumeValue(): Boolean {
         var current = currentPosition
         if (current == -1) return false
+        val source = source
         while (current < source.length) {
             val c = source[current]
             // Inlined skipWhitespaces without field spill and nested loop. Also faster then char2TokenClass
-            if (c == ' ' || c == '\n' || c == '\r' || c == '\t') {
+            if (c.isWs()) {
                 ++current
                 continue
             }
@@ -50,11 +49,12 @@
     override fun skipWhitespaces(): Int {
         var current = currentPosition
         if (current == -1) return current
+        val source = source
         // Skip whitespaces
         while (current < source.length) {
             val c = source[current]
             // Faster than char2TokenClass actually
-            if (c == ' ' || c == '\n' || c == '\r' || c == '\t') {
+            if (c.isWs()) {
                 ++current
             } else {
                 break
@@ -67,9 +67,11 @@
     override fun consumeNextToken(expected: Char) {
         if (currentPosition == -1) unexpectedToken(expected)
         val source = source
-        while (currentPosition < source.length) {
-            val c = source[currentPosition++]
-            if (c == ' ' || c == '\n' || c == '\r' || c == '\t') continue
+        var cpos = currentPosition
+        while (cpos < source.length) {
+            val c = source[cpos++]
+            if (c.isWs()) continue
+            currentPosition = cpos
             if (c == expected) return
             unexpectedToken(expected)
         }
diff --git a/formats/json/jsMain/src/kotlinx/serialization/json/internal/DynamicDecoders.kt b/formats/json/jsMain/src/kotlinx/serialization/json/internal/DynamicDecoders.kt
index 1ff1e40..fc9a523 100644
--- a/formats/json/jsMain/src/kotlinx/serialization/json/internal/DynamicDecoders.kt
+++ b/formats/json/jsMain/src/kotlinx/serialization/json/internal/DynamicDecoders.kt
@@ -68,7 +68,7 @@
     }
 
     override fun <T> decodeSerializableValue(deserializer: DeserializationStrategy<T>): T {
-        return decodeSerializableValuePolymorphic(deserializer)
+        return decodeSerializableValuePolymorphic(deserializer, ::renderTagStack)
     }
 
     private fun coerceInputValue(descriptor: SerialDescriptor, index: Int, tag: String): Boolean =
@@ -248,7 +248,7 @@
         if (isKey) {
             val value = decodeTaggedValue(tag)
             if (value !is String) return decode(tag)
-            return value.toString().cast() ?: throwIllegalKeyType(tag, value, type)
+            return value.cast() ?: throwIllegalKeyType(tag, value, type)
         }
         return decode(tag)
     }
diff --git a/formats/json/jsMain/src/kotlinx/serialization/json/internal/DynamicEncoders.kt b/formats/json/jsMain/src/kotlinx/serialization/json/internal/DynamicEncoders.kt
index 4c4841d..16da5a5 100644
--- a/formats/json/jsMain/src/kotlinx/serialization/json/internal/DynamicEncoders.kt
+++ b/formats/json/jsMain/src/kotlinx/serialization/json/internal/DynamicEncoders.kt
@@ -62,6 +62,8 @@
      * Flag of usage polymorphism with discriminator attribute
      */
     private var polymorphicDiscriminator: String? = null
+    private var polymorphicSerialName: String? = null
+
 
     private object NoOutputMark
 
@@ -163,6 +165,9 @@
     }
 
     override fun encodeJsonElement(element: JsonElement) {
+        if (polymorphicDiscriminator != null && element !is JsonObject) {
+            throwJsonElementPolymorphicException(polymorphicSerialName, element)
+        }
         encodeSerializableValue(JsonElementSerializer, element)
     }
 
@@ -183,8 +188,9 @@
     private fun isNotStructured() = result === NoOutputMark
 
     override fun <T> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) {
-        encodePolymorphically(serializer, value) {
-            polymorphicDiscriminator = it
+        encodePolymorphically(serializer, value) { discriminatorName, serialName ->
+            polymorphicDiscriminator = discriminatorName
+            polymorphicSerialName = serialName
         }
     }
 
@@ -209,8 +215,9 @@
         }
 
         if (polymorphicDiscriminator != null) {
-            current.jsObject[polymorphicDiscriminator!!] = descriptor.serialName
+            current.jsObject[polymorphicDiscriminator!!] = polymorphicSerialName ?: descriptor.serialName
             polymorphicDiscriminator = null
+            polymorphicSerialName = null
         }
 
         current.index = 0
diff --git a/formats/properties/api/kotlinx-serialization-properties.api b/formats/properties/api/kotlinx-serialization-properties.api
index 9f15dc0..258a7f3 100644
--- a/formats/properties/api/kotlinx-serialization-properties.api
+++ b/formats/properties/api/kotlinx-serialization-properties.api
@@ -13,6 +13,5 @@
 
 public final class kotlinx/serialization/properties/PropertiesKt {
 	public static final fun Properties (Lkotlinx/serialization/modules/SerializersModule;)Lkotlinx/serialization/properties/Properties;
-	public static final fun noImpl ()Ljava/lang/Void;
 }
 
diff --git a/formats/properties/api/kotlinx-serialization-properties.klib.api b/formats/properties/api/kotlinx-serialization-properties.klib.api
new file mode 100644
index 0000000..b995c22
--- /dev/null
+++ b/formats/properties/api/kotlinx-serialization-properties.klib.api
@@ -0,0 +1,25 @@
+// Klib ABI Dump
+// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, js, linuxArm32Hfp, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, wasmJs, wasmWasi, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64]
+// Rendering settings:
+// - Signature version: 2
+// - Show manifest properties: true
+// - Show declarations: true
+
+// Library unique name: <org.jetbrains.kotlinx:kotlinx-serialization-properties>
+sealed class kotlinx.serialization.properties/Properties : kotlinx.serialization/SerialFormat { // kotlinx.serialization.properties/Properties|null[0]
+    open val serializersModule // kotlinx.serialization.properties/Properties.serializersModule|{}serializersModule[0]
+        open fun <get-serializersModule>(): kotlinx.serialization.modules/SerializersModule // kotlinx.serialization.properties/Properties.serializersModule.<get-serializersModule>|<get-serializersModule>(){}[0]
+
+    final fun <#A1: kotlin/Any?> decodeFromMap(kotlinx.serialization/DeserializationStrategy<#A1>, kotlin.collections/Map<kotlin/String, kotlin/Any>): #A1 // kotlinx.serialization.properties/Properties.decodeFromMap|decodeFromMap(kotlinx.serialization.DeserializationStrategy<0:0>;kotlin.collections.Map<kotlin.String,kotlin.Any>){0§<kotlin.Any?>}[0]
+    final fun <#A1: kotlin/Any?> decodeFromStringMap(kotlinx.serialization/DeserializationStrategy<#A1>, kotlin.collections/Map<kotlin/String, kotlin/String>): #A1 // kotlinx.serialization.properties/Properties.decodeFromStringMap|decodeFromStringMap(kotlinx.serialization.DeserializationStrategy<0:0>;kotlin.collections.Map<kotlin.String,kotlin.String>){0§<kotlin.Any?>}[0]
+    final fun <#A1: kotlin/Any?> encodeToMap(kotlinx.serialization/SerializationStrategy<#A1>, #A1): kotlin.collections/Map<kotlin/String, kotlin/Any> // kotlinx.serialization.properties/Properties.encodeToMap|encodeToMap(kotlinx.serialization.SerializationStrategy<0:0>;0:0){0§<kotlin.Any?>}[0]
+    final fun <#A1: kotlin/Any?> encodeToStringMap(kotlinx.serialization/SerializationStrategy<#A1>, #A1): kotlin.collections/Map<kotlin/String, kotlin/String> // kotlinx.serialization.properties/Properties.encodeToStringMap|encodeToStringMap(kotlinx.serialization.SerializationStrategy<0:0>;0:0){0§<kotlin.Any?>}[0]
+
+    final object Default : kotlinx.serialization.properties/Properties // kotlinx.serialization.properties/Properties.Default|null[0]
+}
+
+final fun kotlinx.serialization.properties/Properties(kotlinx.serialization.modules/SerializersModule): kotlinx.serialization.properties/Properties // kotlinx.serialization.properties/Properties|Properties(kotlinx.serialization.modules.SerializersModule){}[0]
+final inline fun <#A: reified kotlin/Any?> (kotlinx.serialization.properties/Properties).kotlinx.serialization.properties/decodeFromMap(kotlin.collections/Map<kotlin/String, kotlin/Any>): #A // kotlinx.serialization.properties/decodeFromMap|[email protected](kotlin.collections.Map<kotlin.String,kotlin.Any>){0§<kotlin.Any?>}[0]
+final inline fun <#A: reified kotlin/Any?> (kotlinx.serialization.properties/Properties).kotlinx.serialization.properties/decodeFromStringMap(kotlin.collections/Map<kotlin/String, kotlin/String>): #A // kotlinx.serialization.properties/decodeFromStringMap|[email protected](kotlin.collections.Map<kotlin.String,kotlin.String>){0§<kotlin.Any?>}[0]
+final inline fun <#A: reified kotlin/Any?> (kotlinx.serialization.properties/Properties).kotlinx.serialization.properties/encodeToMap(#A): kotlin.collections/Map<kotlin/String, kotlin/Any> // kotlinx.serialization.properties/encodeToMap|[email protected](0:0){0§<kotlin.Any?>}[0]
+final inline fun <#A: reified kotlin/Any?> (kotlinx.serialization.properties/Properties).kotlinx.serialization.properties/encodeToStringMap(#A): kotlin.collections/Map<kotlin/String, kotlin/String> // kotlinx.serialization.properties/encodeToStringMap|[email protected](0:0){0§<kotlin.Any?>}[0]
diff --git a/formats/properties/build.gradle b/formats/properties/build.gradle
deleted file mode 100644
index dd77ce5..0000000
--- a/formats/properties/build.gradle
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-apply plugin: 'kotlin-multiplatform'
-apply plugin: 'kotlinx-serialization'
-apply from: rootProject.file("gradle/native-targets.gradle")
-apply from: rootProject.file("gradle/configure-source-sets.gradle")
-
-
-kotlin {
-
-    sourceSets {
-        commonMain {
-            dependencies {
-                api project(":kotlinx-serialization-core")
-            }
-        }
-
-        jvmTest {
-            dependencies {
-                implementation 'io.kotlintest:kotlintest:2.0.7'
-                implementation 'com.upokecenter:cbor:4.0.0-beta1'
-                implementation "com.fasterxml.jackson.core:jackson-core:$jackson_version"
-                implementation "com.fasterxml.jackson.core:jackson-databind:$jackson_version"
-                implementation "com.fasterxml.jackson.module:jackson-module-kotlin:$jackson_version"
-                implementation "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor:$jackson_version"
-            }
-        }
-    }
-}
-
-Java9Modularity.configureJava9ModuleInfo(project)
diff --git a/formats/properties/build.gradle.kts b/formats/properties/build.gradle.kts
new file mode 100644
index 0000000..20c8ea9
--- /dev/null
+++ b/formats/properties/build.gradle.kts
@@ -0,0 +1,38 @@
+import Java9Modularity.configureJava9ModuleInfo
+
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+plugins {
+    kotlin("multiplatform")
+
+    alias(libs.plugins.serialization)
+
+    id("native-targets-conventions")
+    id("source-sets-conventions")
+}
+
+kotlin {
+
+    sourceSets {
+        commonMain {
+            dependencies {
+                api(project(":kotlinx-serialization-core"))
+            }
+        }
+
+        jvmTest {
+            dependencies {
+                implementation(libs.kotlintest)
+                implementation(libs.cbor)
+                implementation(libs.jackson.core)
+                implementation(libs.jackson.databind)
+                implementation(libs.jackson.module.kotlin)
+                implementation(libs.jackson.cbor)
+            }
+        }
+    }
+}
+
+configureJava9ModuleInfo()
diff --git a/formats/properties/commonMain/src/kotlinx/serialization/properties/Properties.kt b/formats/properties/commonMain/src/kotlinx/serialization/properties/Properties.kt
index 8760950..f2269b1 100644
--- a/formats/properties/commonMain/src/kotlinx/serialization/properties/Properties.kt
+++ b/formats/properties/commonMain/src/kotlinx/serialization/properties/Properties.kt
@@ -258,8 +258,3 @@
 @ExperimentalSerializationApi
 public inline fun <reified T> Properties.decodeFromStringMap(map: Map<String, String>): T =
     decodeFromStringMap(serializersModule.serializer(), map)
-
-// Migrations below
-
-@PublishedApi
-internal fun noImpl(): Nothing = throw UnsupportedOperationException("Not implemented, should not be called")
diff --git a/formats/properties/commonTest/src/kotlinx/serialization/properties/PropertiesTest.kt b/formats/properties/commonTest/src/kotlinx/serialization/properties/PropertiesTest.kt
index 09af673..3e302a5 100644
--- a/formats/properties/commonTest/src/kotlinx/serialization/properties/PropertiesTest.kt
+++ b/formats/properties/commonTest/src/kotlinx/serialization/properties/PropertiesTest.kt
@@ -7,6 +7,7 @@
 
 import kotlinx.serialization.*
 import kotlinx.serialization.builtins.*
+import kotlinx.serialization.modules.*
 import kotlin.test.*
 
 class PropertiesTest {
@@ -108,6 +109,12 @@
     }
 
     @Test
+    fun testUnitIsEmptyMapModule() {
+        val module = SerializersModule {}
+        assertEquals(emptyMap(), Properties(module).encodeToMap(Unit.serializer(), Unit))
+    }
+
+    @Test
     fun testList() {
         val data = Data(listOf("element1"), "property")
         assertMappedAndRestored(
diff --git a/formats/protobuf/api/kotlinx-serialization-protobuf.api b/formats/protobuf/api/kotlinx-serialization-protobuf.api
index c0d61b9..682e4bf 100644
--- a/formats/protobuf/api/kotlinx-serialization-protobuf.api
+++ b/formats/protobuf/api/kotlinx-serialization-protobuf.api
@@ -39,6 +39,13 @@
 	public final synthetic fun number ()I
 }
 
+public abstract interface annotation class kotlinx/serialization/protobuf/ProtoOneOf : java/lang/annotation/Annotation {
+}
+
+public synthetic class kotlinx/serialization/protobuf/ProtoOneOf$Impl : kotlinx/serialization/protobuf/ProtoOneOf {
+	public fun <init> ()V
+}
+
 public abstract interface annotation class kotlinx/serialization/protobuf/ProtoPacked : java/lang/annotation/Annotation {
 }
 
diff --git a/formats/protobuf/api/kotlinx-serialization-protobuf.klib.api b/formats/protobuf/api/kotlinx-serialization-protobuf.klib.api
new file mode 100644
index 0000000..4e61b61
--- /dev/null
+++ b/formats/protobuf/api/kotlinx-serialization-protobuf.klib.api
@@ -0,0 +1,67 @@
+// Klib ABI Dump
+// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, js, linuxArm32Hfp, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, wasmJs, wasmWasi, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64]
+// Rendering settings:
+// - Signature version: 2
+// - Show manifest properties: true
+// - Show declarations: true
+
+// Library unique name: <org.jetbrains.kotlinx:kotlinx-serialization-protobuf>
+open annotation class kotlinx.serialization.protobuf/ProtoNumber : kotlin/Annotation { // kotlinx.serialization.protobuf/ProtoNumber|null[0]
+    constructor <init>(kotlin/Int) // kotlinx.serialization.protobuf/ProtoNumber.<init>|<init>(kotlin.Int){}[0]
+
+    final val number // kotlinx.serialization.protobuf/ProtoNumber.number|{}number[0]
+        final fun <get-number>(): kotlin/Int // kotlinx.serialization.protobuf/ProtoNumber.number.<get-number>|<get-number>(){}[0]
+}
+
+open annotation class kotlinx.serialization.protobuf/ProtoOneOf : kotlin/Annotation { // kotlinx.serialization.protobuf/ProtoOneOf|null[0]
+    constructor <init>() // kotlinx.serialization.protobuf/ProtoOneOf.<init>|<init>(){}[0]
+}
+
+open annotation class kotlinx.serialization.protobuf/ProtoPacked : kotlin/Annotation { // kotlinx.serialization.protobuf/ProtoPacked|null[0]
+    constructor <init>() // kotlinx.serialization.protobuf/ProtoPacked.<init>|<init>(){}[0]
+}
+
+open annotation class kotlinx.serialization.protobuf/ProtoType : kotlin/Annotation { // kotlinx.serialization.protobuf/ProtoType|null[0]
+    constructor <init>(kotlinx.serialization.protobuf/ProtoIntegerType) // kotlinx.serialization.protobuf/ProtoType.<init>|<init>(kotlinx.serialization.protobuf.ProtoIntegerType){}[0]
+
+    final val type // kotlinx.serialization.protobuf/ProtoType.type|{}type[0]
+        final fun <get-type>(): kotlinx.serialization.protobuf/ProtoIntegerType // kotlinx.serialization.protobuf/ProtoType.type.<get-type>|<get-type>(){}[0]
+}
+
+final enum class kotlinx.serialization.protobuf/ProtoIntegerType : kotlin/Enum<kotlinx.serialization.protobuf/ProtoIntegerType> { // kotlinx.serialization.protobuf/ProtoIntegerType|null[0]
+    enum entry DEFAULT // kotlinx.serialization.protobuf/ProtoIntegerType.DEFAULT|null[0]
+    enum entry FIXED // kotlinx.serialization.protobuf/ProtoIntegerType.FIXED|null[0]
+    enum entry SIGNED // kotlinx.serialization.protobuf/ProtoIntegerType.SIGNED|null[0]
+
+    final val entries // kotlinx.serialization.protobuf/ProtoIntegerType.entries|#static{}entries[0]
+        final fun <get-entries>(): kotlin.enums/EnumEntries<kotlinx.serialization.protobuf/ProtoIntegerType> // kotlinx.serialization.protobuf/ProtoIntegerType.entries.<get-entries>|<get-entries>#static(){}[0]
+
+    final fun valueOf(kotlin/String): kotlinx.serialization.protobuf/ProtoIntegerType // kotlinx.serialization.protobuf/ProtoIntegerType.valueOf|valueOf#static(kotlin.String){}[0]
+    final fun values(): kotlin/Array<kotlinx.serialization.protobuf/ProtoIntegerType> // kotlinx.serialization.protobuf/ProtoIntegerType.values|values#static(){}[0]
+}
+
+final class kotlinx.serialization.protobuf/ProtoBufBuilder { // kotlinx.serialization.protobuf/ProtoBufBuilder|null[0]
+    final var encodeDefaults // kotlinx.serialization.protobuf/ProtoBufBuilder.encodeDefaults|{}encodeDefaults[0]
+        final fun <get-encodeDefaults>(): kotlin/Boolean // kotlinx.serialization.protobuf/ProtoBufBuilder.encodeDefaults.<get-encodeDefaults>|<get-encodeDefaults>(){}[0]
+        final fun <set-encodeDefaults>(kotlin/Boolean) // kotlinx.serialization.protobuf/ProtoBufBuilder.encodeDefaults.<set-encodeDefaults>|<set-encodeDefaults>(kotlin.Boolean){}[0]
+    final var serializersModule // kotlinx.serialization.protobuf/ProtoBufBuilder.serializersModule|{}serializersModule[0]
+        final fun <get-serializersModule>(): kotlinx.serialization.modules/SerializersModule // kotlinx.serialization.protobuf/ProtoBufBuilder.serializersModule.<get-serializersModule>|<get-serializersModule>(){}[0]
+        final fun <set-serializersModule>(kotlinx.serialization.modules/SerializersModule) // kotlinx.serialization.protobuf/ProtoBufBuilder.serializersModule.<set-serializersModule>|<set-serializersModule>(kotlinx.serialization.modules.SerializersModule){}[0]
+}
+
+sealed class kotlinx.serialization.protobuf/ProtoBuf : kotlinx.serialization/BinaryFormat { // kotlinx.serialization.protobuf/ProtoBuf|null[0]
+    open val serializersModule // kotlinx.serialization.protobuf/ProtoBuf.serializersModule|{}serializersModule[0]
+        open fun <get-serializersModule>(): kotlinx.serialization.modules/SerializersModule // kotlinx.serialization.protobuf/ProtoBuf.serializersModule.<get-serializersModule>|<get-serializersModule>(){}[0]
+
+    open fun <#A1: kotlin/Any?> decodeFromByteArray(kotlinx.serialization/DeserializationStrategy<#A1>, kotlin/ByteArray): #A1 // kotlinx.serialization.protobuf/ProtoBuf.decodeFromByteArray|decodeFromByteArray(kotlinx.serialization.DeserializationStrategy<0:0>;kotlin.ByteArray){0§<kotlin.Any?>}[0]
+    open fun <#A1: kotlin/Any?> encodeToByteArray(kotlinx.serialization/SerializationStrategy<#A1>, #A1): kotlin/ByteArray // kotlinx.serialization.protobuf/ProtoBuf.encodeToByteArray|encodeToByteArray(kotlinx.serialization.SerializationStrategy<0:0>;0:0){0§<kotlin.Any?>}[0]
+
+    final object Default : kotlinx.serialization.protobuf/ProtoBuf // kotlinx.serialization.protobuf/ProtoBuf.Default|null[0]
+}
+
+final object kotlinx.serialization.protobuf.schema/ProtoBufSchemaGenerator { // kotlinx.serialization.protobuf.schema/ProtoBufSchemaGenerator|null[0]
+    final fun generateSchemaText(kotlin.collections/List<kotlinx.serialization.descriptors/SerialDescriptor>, kotlin/String? = ..., kotlin.collections/Map<kotlin/String, kotlin/String> = ...): kotlin/String // kotlinx.serialization.protobuf.schema/ProtoBufSchemaGenerator.generateSchemaText|generateSchemaText(kotlin.collections.List<kotlinx.serialization.descriptors.SerialDescriptor>;kotlin.String?;kotlin.collections.Map<kotlin.String,kotlin.String>){}[0]
+    final fun generateSchemaText(kotlinx.serialization.descriptors/SerialDescriptor, kotlin/String? = ..., kotlin.collections/Map<kotlin/String, kotlin/String> = ...): kotlin/String // kotlinx.serialization.protobuf.schema/ProtoBufSchemaGenerator.generateSchemaText|generateSchemaText(kotlinx.serialization.descriptors.SerialDescriptor;kotlin.String?;kotlin.collections.Map<kotlin.String,kotlin.String>){}[0]
+}
+
+final fun kotlinx.serialization.protobuf/ProtoBuf(kotlinx.serialization.protobuf/ProtoBuf = ..., kotlin/Function1<kotlinx.serialization.protobuf/ProtoBufBuilder, kotlin/Unit>): kotlinx.serialization.protobuf/ProtoBuf // kotlinx.serialization.protobuf/ProtoBuf|ProtoBuf(kotlinx.serialization.protobuf.ProtoBuf;kotlin.Function1<kotlinx.serialization.protobuf.ProtoBufBuilder,kotlin.Unit>){}[0]
diff --git a/formats/protobuf/build.gradle b/formats/protobuf/build.gradle
deleted file mode 100644
index 9f93b18..0000000
--- a/formats/protobuf/build.gradle
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-apply plugin: 'java' // Needed for protobuf plugin only
-apply plugin: 'kotlin-multiplatform'
-apply plugin: 'kotlinx-serialization'
-apply plugin: 'com.google.protobuf'
-apply from: rootProject.file("gradle/native-targets.gradle")
-apply from: rootProject.file("gradle/configure-source-sets.gradle")
-
-protobuf {
-    protoc {
-        // Download from repositories
-        artifact = 'com.google.protobuf:protoc:3.17.3'
-    }
-}
-
-clean {
-    delete protobuf.generatedFilesBaseDir
-}
-
-kotlin {
-    sourceSets {
-        configureEach {
-            languageSettings.optIn("kotlinx.serialization.internal.CoreFriendModuleApi")
-        }
-
-        commonMain {
-            dependencies {
-                api project(":kotlinx-serialization-core")
-            }
-        }
-
-        jvmTest {
-            kotlin.srcDirs += file("${protobuf.generatedFilesBaseDir}/test/java")
-            dependencies {
-                implementation 'com.google.protobuf:protobuf-java:3.17.3'
-                implementation 'io.kotlintest:kotlintest:2.0.7'
-            }
-        }
-    }
-}
-
-sourceSets.test.proto {
-    srcDirs = ['testProto', 'jvmTest/resources/common']
-}
-
-compileTestKotlinJvm {
-    dependsOn 'generateTestProto'
-}
-
-Java9Modularity.configureJava9ModuleInfo(project)
diff --git a/formats/protobuf/build.gradle.kts b/formats/protobuf/build.gradle.kts
new file mode 100644
index 0000000..1ff991d
--- /dev/null
+++ b/formats/protobuf/build.gradle.kts
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+import Java9Modularity.configureJava9ModuleInfo
+import com.google.protobuf.gradle.*
+import org.gradle.kotlin.dsl.protobuf
+
+plugins {
+    java // Needed for protobuf plugin only
+    kotlin("multiplatform")
+
+    alias(libs.plugins.serialization)
+    alias(libs.plugins.protobuf)
+
+    id("native-targets-conventions")
+    id("source-sets-conventions")
+}
+
+protobuf {
+    protobuf.protoc {
+        // Download from repositories
+        artifact = libs.protoc.get().toString()
+    }
+}
+
+tasks.clean {
+    delete(protobuf.protobuf.generatedFilesBaseDir)
+}
+
+kotlin {
+    sourceSets {
+        configureEach {
+            languageSettings.optIn("kotlinx.serialization.internal.CoreFriendModuleApi")
+        }
+
+        commonMain {
+            dependencies {
+                api(project(":kotlinx-serialization-core"))
+            }
+        }
+
+        jvmTest {
+            kotlin.srcDirs(file("${protobuf.protobuf.generatedFilesBaseDir}/test/java"))
+
+            dependencies {
+                implementation(libs.protobuf.java)
+                implementation(libs.kotlintest)
+            }
+        }
+    }
+}
+
+sourceSets.test {
+    extensions.configure<SourceDirectorySet>("proto") {
+        srcDirs("testProto", "jvmTest/resources/common")
+    }
+}
+
+tasks.compileTestKotlinJvm {
+    dependsOn("generateTestProto")
+}
+
+configureJava9ModuleInfo()
diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/ProtoTypes.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/ProtoTypes.kt
index 109ffb8..e673f16 100644
--- a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/ProtoTypes.kt
+++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/ProtoTypes.kt
@@ -53,3 +53,14 @@
 @Target(AnnotationTarget.PROPERTY)
 @ExperimentalSerializationApi
 public annotation class ProtoPacked
+
+/**
+ * Instructs that a particular property should be written as an [oneof](https://protobuf.dev/programming-guides/proto2/#oneof).
+ *
+ * The type of the annotated property should be polymorphic (interface or abstract class).
+ * Inheritors of this type would represent `one of` choices, and each inheritor should have exactly one property, annotated with [ProtoNumber].
+ */
+@SerialInfo
+@Target(AnnotationTarget.PROPERTY)
+@ExperimentalSerializationApi
+public annotation class ProtoOneOf
\ No newline at end of file
diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/Helpers.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/Helpers.kt
index 59533db..ea6d4b6 100644
--- a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/Helpers.kt
+++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/Helpers.kt
@@ -8,27 +8,45 @@
 
 import kotlinx.serialization.*
 import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.modules.*
 import kotlinx.serialization.protobuf.*
 
 internal typealias ProtoDesc = Long
-internal const val VARINT = 0
-internal const val i64 = 1
-internal const val SIZE_DELIMITED = 2
-internal const val i32 = 5
 
-private const val INTTYPEMASK = (Int.MAX_VALUE.toLong() shr 1) shl 33
+internal enum class ProtoWireType(val typeId: Int) {
+    INVALID(-1),
+    VARINT(0),
+    i64(1),
+    SIZE_DELIMITED(2),
+    i32(5),
+    ;
+
+    companion object {
+        fun from(typeId: Int): ProtoWireType {
+            return ProtoWireType.entries.find { it.typeId == typeId } ?: INVALID
+        }
+    }
+
+    fun wireIntWithTag(tag: Int): Int {
+        return ((tag shl 3) or typeId)
+    }
+
+    override fun toString(): String {
+        return "${this.name}($typeId)"
+    }
+}
+
+internal const val ID_HOLDER_ONE_OF = -2
+
+private const val ONEOFMASK = 1L shl 36
+private const val INTTYPEMASK = 3L shl 33
 private const val PACKEDMASK = 1L shl 32
 
 @Suppress("NOTHING_TO_INLINE")
-internal inline fun ProtoDesc(protoId: Int, type: ProtoIntegerType, packed: Boolean): ProtoDesc {
-    val packedBits = if (packed) 1L shl 32 else 0L
-    val signature = type.signature or packedBits
-    return signature or protoId.toLong()
-}
-
-@Suppress("NOTHING_TO_INLINE")
-internal inline fun ProtoDesc(protoId: Int, type: ProtoIntegerType): ProtoDesc {
-    return type.signature or protoId.toLong()
+internal inline fun ProtoDesc(protoId: Int, type: ProtoIntegerType, packed: Boolean = false, oneOf: Boolean = false): ProtoDesc {
+    val packedBits = if (packed) PACKEDMASK else 0L
+    val oneOfBits = if (oneOf) ONEOFMASK else 0L
+    return packedBits or oneOfBits or type.signature or protoId.toLong()
 }
 
 internal inline val ProtoDesc.protoId: Int get() = (this and Int.MAX_VALUE.toLong()).toInt()
@@ -51,37 +69,94 @@
 internal val ProtoDesc.isPacked: Boolean
     get() = (this and PACKEDMASK) != 0L
 
+internal val ProtoDesc.isOneOf: Boolean
+    get() = (this and ONEOFMASK) != 0L
+
+internal fun ProtoDesc.overrideId(protoId: Int): ProtoDesc {
+    return this and (0xFFFFFFF00000000L) or protoId.toLong()
+}
+
 internal fun SerialDescriptor.extractParameters(index: Int): ProtoDesc {
     val annotations = getElementAnnotations(index)
     var protoId: Int = index + 1
     var format: ProtoIntegerType = ProtoIntegerType.DEFAULT
     var protoPacked = false
+    var isOneOf = false
 
     for (i in annotations.indices) { // Allocation-friendly loop
         val annotation = annotations[i]
         if (annotation is ProtoNumber) {
             protoId = annotation.number
+            checkFieldNumber(protoId, i, this)
         } else if (annotation is ProtoType) {
             format = annotation.type
         } else if (annotation is ProtoPacked) {
             protoPacked = true
+        } else if (annotation is ProtoOneOf) {
+            isOneOf = true
         }
     }
-    return ProtoDesc(protoId, format, protoPacked)
+    if (isOneOf) {
+        // reset protoId to index-based for oneOf field,
+        // Decoder will restore the real proto id then from [ProtobufDecoder.index2IdMap]
+        // See [kotlinx.serialization.protobuf.internal.ProtobufDecoder.decodeElementIndex] for detail
+        protoId = index + 1
+    }
+    return ProtoDesc(protoId, format, protoPacked, isOneOf)
 }
 
+/**
+ * Get the proto id from the descriptor of [index] element,
+ * or return [ID_HOLDER_ONE_OF] if such element is marked with [ProtoOneOf]
+ */
 internal fun extractProtoId(descriptor: SerialDescriptor, index: Int, zeroBasedDefault: Boolean): Int {
     val annotations = descriptor.getElementAnnotations(index)
+    var result = if (zeroBasedDefault) index else index + 1
     for (i in annotations.indices) { // Allocation-friendly loop
         val annotation = annotations[i]
-        if (annotation is ProtoNumber) {
-            return annotation.number
+        if (annotation is ProtoOneOf) {
+            // Fast return for one of field
+            return ID_HOLDER_ONE_OF
+        } else if (annotation is ProtoNumber) {
+            result = annotation.number
+            // 0 or negative numbers are acceptable for enums
+            if (!zeroBasedDefault) {
+                checkFieldNumber(result, i, descriptor)
+            }
         }
     }
-    return if (zeroBasedDefault) index else index + 1
+    return result
 }
 
-internal class ProtobufDecodingException(message: String) : SerializationException(message)
+private fun checkFieldNumber(fieldNumber: Int, propertyIndex: Int, descriptor: SerialDescriptor) {
+    if (fieldNumber <= 0) {
+        throw SerializationException("$fieldNumber is not allowed in ProtoNumber for property '${descriptor.getElementName(propertyIndex)}' of '${descriptor.serialName}', because protobuf supports field numbers in range 1..${Int.MAX_VALUE}")
+    }
+}
+
+internal class ProtobufDecodingException(message: String, e: Throwable? = null) : SerializationException(message, e)
 
 internal expect fun Int.reverseBytes(): Int
 internal expect fun Long.reverseBytes(): Long
+
+
+internal fun SerialDescriptor.getAllOneOfSerializerOfField(
+    serializersModule: SerializersModule,
+): List<SerialDescriptor> {
+    return when (this.kind) {
+        PolymorphicKind.OPEN -> serializersModule.getPolymorphicDescriptors(this)
+        PolymorphicKind.SEALED -> getElementDescriptor(1).elementDescriptors.toList()
+        else -> throw IllegalArgumentException("Class ${this.serialName} should be abstract or sealed or interface to be used as @ProtoOneOf property.")
+    }.onEach { desc ->
+        if (desc.getElementAnnotations(0).none { anno -> anno is ProtoNumber }) {
+            throw IllegalArgumentException("${desc.serialName} implementing oneOf type ${this.serialName} should have @ProtoNumber annotation in its single property.")
+        }
+    }
+}
+
+internal fun SerialDescriptor.getActualOneOfSerializer(
+    serializersModule: SerializersModule,
+    protoId: Int
+): SerialDescriptor? {
+    return getAllOneOfSerializerOfField(serializersModule).find { it.extractParameters(0).protoId == protoId }
+}
diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufDecoding.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufDecoding.kt
index 861e2bf..56884b1 100644
--- a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufDecoding.kt
+++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufDecoding.kt
@@ -28,6 +28,9 @@
     private var indexCache: IntArray? = null
     private var sparseIndexCache: MutableMap<Int, Int>? = null
 
+    // Index -> proto id for oneof element. An oneof element of certain index may refer to different proto id in runtime.
+    private var index2IdMap: MutableMap<Int, Int>? = null
+
     private var nullValue: Boolean = false
     private val elementMarker = ElementMarker(descriptor, ::readIfAbsent)
 
@@ -42,11 +45,17 @@
              * If we have reasonably small count of elements, try to build sequential
              * array for the fast-path. Fast-path implies that elements are not marked with @ProtoId
              * explicitly or are monotonic and incremental (maybe, 1-indexed)
+             *
+             * Initialize all elements, because there will always be one extra element as arrays are numbered from 0
+             * but in protobuf field number starts from 1.
              */
-            val cache = IntArray(elements + 1)
+            val cache = IntArray(elements + 1) { -1 }
             for (i in 0 until elements) {
                 val protoId = extractProtoId(descriptor, i, false)
-                if (protoId <= elements) {
+                // If any element is marked as ProtoOneOf,
+                // the fast path is not applicable
+                // because it will contain more id than elements
+                if (protoId <= elements && protoId != ID_HOLDER_ONE_OF) {
                     cache[protoId] = i
                 } else {
                     return populateCacheMap(descriptor, elements)
@@ -59,22 +68,39 @@
     }
 
     private fun populateCacheMap(descriptor: SerialDescriptor, elements: Int) {
-        val map = HashMap<Int, Int>(elements)
+        val map = HashMap<Int, Int>(elements, 1f)
+        var oneOfCount = 0
         for (i in 0 until elements) {
-            map[extractProtoId(descriptor, i, false)] = i
+            val id = extractProtoId(descriptor, i, false)
+            if (id == ID_HOLDER_ONE_OF) {
+                descriptor.getElementDescriptor(i)
+                    .getAllOneOfSerializerOfField(serializersModule)
+                    .map { it.extractParameters(0).protoId }
+                    .forEach { map.putProtoId(it, i) }
+                oneOfCount ++
+            } else {
+                map.putProtoId(extractProtoId(descriptor, i, false),  i)
+            }
+        }
+        if (oneOfCount > 0) {
+            index2IdMap = HashMap(oneOfCount, 1f)
         }
         sparseIndexCache = map
     }
 
-    private fun getIndexByTag(protoTag: Int): Int {
-        val array = indexCache
-        if (array != null) {
-            return array.getOrElse(protoTag) { -1 }
-        }
-        return getIndexByTagSlowPath(protoTag)
+    private fun MutableMap<Int, Int>.putProtoId(protoId: Int, index: Int) {
+        put(protoId, index)
     }
 
-    private fun getIndexByTagSlowPath(
+    private fun getIndexByNum(protoNum: Int): Int {
+        val array = indexCache
+        if (array != null) {
+            return array.getOrElse(protoNum) { -1 }
+        }
+        return getIndexByNumSlowPath(protoNum)
+    }
+
+    private fun getIndexByNumSlowPath(
         protoTag: Int
     ): Int = sparseIndexCache!!.getOrElse(protoTag) { -1 }
 
@@ -99,32 +125,53 @@
     }
 
     override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
-        return when (descriptor.kind) {
-            StructureKind.LIST -> {
-                val tag = currentTagOrDefault
-                return if (this.descriptor.kind == StructureKind.LIST && tag != MISSING_TAG && this.descriptor != descriptor) {
-                    val reader = makeDelimited(reader, tag)
-                    // repeated decoder expects the first tag to be read already
-                    reader.readTag()
-                    // all elements always have id = 1
-                    RepeatedDecoder(proto, reader, ProtoDesc(1, ProtoIntegerType.DEFAULT), descriptor)
+        return try {
+            when (descriptor.kind) {
+                StructureKind.LIST -> {
+                    val tag = currentTagOrDefault
+                    return if (this.descriptor.kind == StructureKind.LIST && tag != MISSING_TAG && this.descriptor != descriptor) {
+                        val reader = makeDelimited(reader, tag)
+                        // repeated decoder expects the first tag to be read already
+                        reader.readTag()
+                        // all elements always have id = 1
+                        RepeatedDecoder(proto, reader, ProtoDesc(1, ProtoIntegerType.DEFAULT), descriptor)
 
-                } else if (reader.currentType == SIZE_DELIMITED && descriptor.getElementDescriptor(0).isPackable) {
-                    val sliceReader = ProtobufReader(reader.objectInput())
-                    PackedArrayDecoder(proto, sliceReader, descriptor)
+                    } else if (reader.currentType == ProtoWireType.SIZE_DELIMITED && descriptor.getElementDescriptor(0).isPackable) {
+                        val sliceReader = ProtobufReader(reader.objectInput())
+                        PackedArrayDecoder(proto, sliceReader, descriptor)
 
-                } else {
-                    RepeatedDecoder(proto, reader, tag, descriptor)
+                    } else {
+                        RepeatedDecoder(proto, reader, tag, descriptor)
+                    }
                 }
+
+                StructureKind.CLASS, StructureKind.OBJECT, is PolymorphicKind -> {
+                    val tag = currentTagOrDefault
+                    // Do not create redundant copy
+                    if (tag == MISSING_TAG && this.descriptor == descriptor) return this
+                    if (tag.isOneOf) {
+                        // If a tag is annotated as oneof
+                        // [tag.protoId] here is overwritten with index-based default id in
+                        // [kotlinx.serialization.protobuf.internal.HelpersKt.extractParameters]
+                        // and restored the real id from index2IdMap, set by [decodeElementIndex]
+                        val rawIndex = tag.protoId - 1
+                        val restoredTag = index2IdMap?.get(rawIndex)?.let { tag.overrideId(it) } ?: tag
+                        return OneOfPolymorphicReader(proto, reader, restoredTag, descriptor)
+                    }
+                    return ProtobufDecoder(proto, makeDelimited(reader, tag), descriptor)
+                }
+
+                StructureKind.MAP -> MapEntryReader(
+                    proto,
+                    makeDelimitedForced(reader, currentTagOrDefault),
+                    currentTagOrDefault,
+                    descriptor
+                )
+
+                else -> throw SerializationException("Primitives are not supported at top-level")
             }
-            StructureKind.CLASS, StructureKind.OBJECT, is PolymorphicKind -> {
-                val tag = currentTagOrDefault
-                // Do not create redundant copy
-                if (tag == MISSING_TAG && this.descriptor == descriptor) return this
-                return ProtobufDecoder(proto, makeDelimited(reader, tag), descriptor)
-            }
-            StructureKind.MAP -> MapEntryReader(proto, makeDelimitedForced(reader, currentTagOrDefault), currentTagOrDefault, descriptor)
-            else -> throw SerializationException("Primitives are not supported at top-level")
+        } catch (e: ProtobufDecodingException) {
+            throw ProtobufDecodingException("Fail to begin structure for ${descriptor.serialName} in ${this.descriptor.serialName} at proto number ${currentTagOrDefault.protoId}", e)
         }
     }
 
@@ -141,41 +188,51 @@
     override fun decodeTaggedByte(tag: ProtoDesc): Byte = decodeTaggedInt(tag).toByte()
     override fun decodeTaggedShort(tag: ProtoDesc): Short = decodeTaggedInt(tag).toShort()
     override fun decodeTaggedInt(tag: ProtoDesc): Int {
-        return if (tag == MISSING_TAG) {
-            reader.readInt32NoTag()
-        } else {
-            reader.readInt(tag.integerType)
+        return decodeOrThrow(tag) {
+            if (tag == MISSING_TAG) {
+                reader.readInt32NoTag()
+            } else {
+                reader.readInt(tag.integerType)
+            }
         }
     }
     override fun decodeTaggedLong(tag: ProtoDesc): Long {
-        return if (tag == MISSING_TAG) {
-            reader.readLongNoTag()
-        } else {
-            reader.readLong(tag.integerType)
+        return decodeOrThrow(tag) {
+            if (tag == MISSING_TAG) {
+                reader.readLongNoTag()
+            } else {
+                reader.readLong(tag.integerType)
+            }
         }
     }
 
     override fun decodeTaggedFloat(tag: ProtoDesc): Float {
-        return if (tag == MISSING_TAG) {
-            reader.readFloatNoTag()
-        } else {
-            reader.readFloat()
+        return decodeOrThrow(tag) {
+            if (tag == MISSING_TAG) {
+                reader.readFloatNoTag()
+            } else {
+                reader.readFloat()
+            }
         }
     }
     override fun decodeTaggedDouble(tag: ProtoDesc): Double {
-        return if (tag == MISSING_TAG) {
-            reader.readDoubleNoTag()
-        } else {
-            reader.readDouble()
+        return decodeOrThrow(tag) {
+            if (tag == MISSING_TAG) {
+                reader.readDoubleNoTag()
+            } else {
+                reader.readDouble()
+            }
         }
     }
     override fun decodeTaggedChar(tag: ProtoDesc): Char = decodeTaggedInt(tag).toChar()
 
     override fun decodeTaggedString(tag: ProtoDesc): String {
-        return if (tag == MISSING_TAG) {
-            reader.readStringNoTag()
-        } else {
-            reader.readString()
+        return decodeOrThrow(tag) {
+            if (tag == MISSING_TAG) {
+                reader.readStringNoTag()
+            } else {
+                reader.readString()
+            }
         }
     }
 
@@ -186,22 +243,49 @@
     override fun <T> decodeSerializableValue(deserializer: DeserializationStrategy<T>): T = decodeSerializableValue(deserializer, null)
 
     @Suppress("UNCHECKED_CAST")
-    override fun <T> decodeSerializableValue(deserializer: DeserializationStrategy<T>, previousValue: T?): T = when {
-        deserializer is MapLikeSerializer<*, *, *, *> -> {
-            deserializeMap(deserializer as DeserializationStrategy<T>, previousValue)
+    override fun <T> decodeSerializableValue(deserializer: DeserializationStrategy<T>, previousValue: T?): T = try {
+        when {
+            deserializer is MapLikeSerializer<*, *, *, *> -> {
+                deserializeMap(deserializer as DeserializationStrategy<T>, previousValue)
+            }
+
+            deserializer.descriptor == ByteArraySerializer().descriptor -> deserializeByteArray(previousValue as ByteArray?) as T
+            deserializer is AbstractCollectionSerializer<*, *, *> ->
+                (deserializer as AbstractCollectionSerializer<*, T, *>).merge(this, previousValue)
+
+            else -> deserializer.deserialize(this)
         }
-        deserializer.descriptor == ByteArraySerializer().descriptor -> deserializeByteArray(previousValue as ByteArray?) as T
-        deserializer is AbstractCollectionSerializer<*, *, *> ->
-            (deserializer as AbstractCollectionSerializer<*, T, *>).merge(this, previousValue)
-        else -> deserializer.deserialize(this)
+    } catch (e: ProtobufDecodingException) {
+        val currentTag = currentTagOrDefault
+        val msg = if (descriptor != deserializer.descriptor) {
+            // Decoding child element
+            if (descriptor.kind == StructureKind.LIST && deserializer.descriptor.kind != StructureKind.MAP) {
+                // Decoding repeated field
+                "Error while decoding index ${currentTag.protoId - 1} in repeated field of ${deserializer.descriptor.serialName}"
+            } else if (descriptor.kind == StructureKind.MAP) {
+                // Decoding map field
+                val index = (currentTag.protoId - 1) / 2
+                val field = if ((currentTag.protoId - 1) % 2 == 0) { "key" } else "value"
+                "Error while decoding $field of index $index in map field of ${deserializer.descriptor.serialName}"
+            } else {
+                // Decoding common class
+                "Error while decoding ${deserializer.descriptor.serialName} at proto number ${currentTag.protoId} of ${descriptor.serialName}"
+            }
+        } else {
+            // Decoding self
+            "Error while decoding ${descriptor.serialName}"
+        }
+        throw ProtobufDecodingException(msg, e)
     }
 
     private fun deserializeByteArray(previousValue: ByteArray?): ByteArray {
         val tag = currentTagOrDefault
-        val array = if (tag == MISSING_TAG) {
-            reader.readByteArrayNoTag()
-        } else {
-            reader.readByteArray()
+        val array = decodeOrThrow(tag) {
+            if (tag == MISSING_TAG) {
+                reader.readByteArrayNoTag()
+            } else {
+                reader.readByteArray()
+            }
         }
         return if (previousValue == null) array else previousValue + array
     }
@@ -220,18 +304,36 @@
     override fun SerialDescriptor.getTag(index: Int) = extractParameters(index)
 
     override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
-        while (true) {
-            val protoId = reader.readTag()
-            if (protoId == -1) { // EOF
-                return elementMarker.nextUnmarkedIndex()
+        try {
+            while (true) {
+                val protoId = reader.readTag()
+                if (protoId == -1) { // EOF
+                    return elementMarker.nextUnmarkedIndex()
+                }
+                if (protoId == 0) {
+                    throw SerializationException("0 is not allowed as the protobuf field number in ${descriptor.serialName}, the input bytes may have been corrupted")
+                }
+                val index = getIndexByNum(protoId)
+                if (index == -1) { // not found
+                    reader.skipElement()
+                } else {
+                    if (descriptor.extractParameters(index).isOneOf) {
+                        /**
+                         * While decoding message with one-of field,
+                         * the proto id read from wire data cannot be easily found
+                         * in the properties of this type,
+                         * So the index of this one-of property and the id read from the wire
+                         * are saved in this map, then restored in [beginStructure]
+                         * and passed to [OneOfPolymorphicReader] to get the actual deserializer.
+                         */
+                        index2IdMap?.put(index, protoId)
+                    }
+                    elementMarker.mark(index)
+                    return index
+                }
             }
-            val index = getIndexByTag(protoId)
-            if (index == -1) { // not found
-                reader.skipElement()
-            } else {
-                elementMarker.mark(index)
-                return index
-            }
+        } catch (e: ProtobufDecodingException) {
+            throw ProtobufDecodingException("Fail to get element index for ${descriptor.serialName} in ${this.descriptor.serialName}", e)
         }
     }
 
@@ -253,6 +355,19 @@
         }
         return false
     }
+
+    private inline fun <T> decodeOrThrow(tag: ProtoDesc, action: (tag: ProtoDesc) -> T): T {
+        try {
+            return action(tag)
+        } catch (e: ProtobufDecodingException) {
+            rethrowException(tag, e)
+        }
+    }
+
+    @Suppress("NOTHING_TO_INLINE")
+    private inline fun rethrowException(tag: ProtoDesc, e: ProtobufDecodingException): Nothing {
+        throw ProtobufDecodingException("Error while decoding proto number ${tag.protoId} of ${descriptor.serialName}", e)
+    }
 }
 
 private class RepeatedDecoder(
@@ -331,6 +446,97 @@
         else ProtoDesc(2, (parentTag.integerType))
 }
 
+private class OneOfPolymorphicReader(
+    proto: ProtoBuf,
+    decoder: ProtobufReader,
+    private val parentTag: ProtoDesc,
+    descriptor: SerialDescriptor
+) : ProtobufDecoder(proto, decoder, descriptor) {
+    private var serialNameDecoded = false
+    private var contentDecoded = false
+    override fun SerialDescriptor.getTag(index: Int): ProtoDesc = if (index == 0) {
+        POLYMORPHIC_NAME_TAG
+    } else {
+        extractParameters(0)
+    }
+
+    override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
+        return if (descriptor == this.descriptor) {
+            this
+        } else {
+            OneOfElementReader(proto, reader, descriptor)
+        }
+    }
+
+    override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+        if (!serialNameDecoded) {
+            serialNameDecoded = true
+            return 0
+        } else if (!contentDecoded) {
+            contentDecoded = true
+            return 1
+        } else {
+            return CompositeDecoder.DECODE_DONE
+        }
+    }
+
+    override fun decodeTaggedString(tag: ProtoDesc): String = if (tag == POLYMORPHIC_NAME_TAG) {
+        // This exception will neven be thrown
+        // Subclass of oneof-field without matching ProtoNum annotated will be skipped in outer [decodeElementIndex]
+        // and raise a [MissingFieldException]
+        descriptor.getActualOneOfSerializer(serializersModule, parentTag.protoId)?.serialName ?: throw SerializationException(
+            "Cannot find a subclass of ${descriptor.serialName} annotated with @ProtoNumber(${parentTag.protoId})."
+        )
+    } else {
+        super.decodeTaggedString(tag)
+    }
+}
+
+private class OneOfElementReader(
+    proto: ProtoBuf,
+    decoder: ProtobufReader,
+    descriptor: SerialDescriptor
+) : ProtobufDecoder(proto, decoder, descriptor) {
+    private val classId: Int
+    init {
+        require(descriptor.elementsCount == 1) {
+            "Implementation of oneOf type ${descriptor.serialName} should contain only 1 element, but get ${descriptor.elementsCount}"
+        }
+        val protoNumber = descriptor.getElementAnnotations(0).filterIsInstance<ProtoNumber>().singleOrNull()
+        require(protoNumber != null) {
+            "Implementation of oneOf type ${descriptor.serialName} should have @ProtoNumber annotation"
+        }
+        classId = protoNumber.number
+    }
+
+    private var contentDecoded: Boolean = false
+
+    override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder {
+        return when(descriptor.kind) {
+            StructureKind.CLASS, StructureKind.OBJECT, is PolymorphicKind -> {
+                val tag = currentTagOrDefault
+                // Do not create redundant copy
+                if (tag == MISSING_TAG && this.descriptor == descriptor) return this
+                if (tag.isOneOf) throw SerializationException("An oneof element cannot be directly child of another oneof element")
+                ProtobufDecoder(proto, makeDelimited(reader, tag), descriptor)
+            }
+            else -> {
+                throw SerializationException("Type ${descriptor.kind} cannot be directly child of oneof element")
+            }
+        }
+    }
+
+    override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+        return if (contentDecoded) {
+            -1
+        }
+        else {
+            contentDecoded = true
+            0
+        }
+    }
+}
+
 private fun makeDelimited(decoder: ProtobufReader, parentTag: ProtoDesc): ProtobufReader {
     val tagless = parentTag == MISSING_TAG
     val input = if (tagless) decoder.objectTaglessInput() else decoder.objectInput()
diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufEncoding.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufEncoding.kt
index fab7a09..b7d5dd2 100644
--- a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufEncoding.kt
+++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufEncoding.kt
@@ -60,9 +60,15 @@
         }
         StructureKind.CLASS, StructureKind.OBJECT, is PolymorphicKind -> {
             val tag = currentTagOrDefault
-            if (tag == MISSING_TAG && descriptor == this.descriptor) this
-            else ObjectEncoder(proto, currentTagOrDefault, writer, descriptor = descriptor)
+            if (tag == MISSING_TAG && descriptor == this.descriptor) {
+                this
+            } else if (tag.isOneOf) {
+                OneOfPolymorphicEncoder(proto = proto, parentWriter = writer, descriptor = descriptor)
+            } else {
+                ObjectEncoder(proto, currentTagOrDefault, writer, descriptor = descriptor)
+            }
         }
+
         StructureKind.MAP -> MapRepeatedEncoder(proto, currentTagOrDefault, writer, descriptor)
         else -> throw SerializationException("This serial kind is not supported as structure: $descriptor")
     }
@@ -117,11 +123,13 @@
         enumDescriptor: SerialDescriptor,
         ordinal: Int
     ) {
+        // An enum element will never be one-of field
+        val id = extractProtoId(enumDescriptor, ordinal, zeroBasedDefault = true)
         if (tag == MISSING_TAG) {
-            writer.writeInt(extractProtoId(enumDescriptor, ordinal, zeroBasedDefault = true))
+            writer.writeInt(id)
         } else {
             writer.writeInt(
-                extractProtoId(enumDescriptor, ordinal, zeroBasedDefault = true),
+                id,
                 tag.protoId,
                 ProtoIntegerType.DEFAULT
             )
@@ -151,7 +159,7 @@
     private fun <T> serializeMap(serializer: SerializationStrategy<T>, value: T) {
         // encode maps as collection of map entries, not merged collection of key-values
         val casted = (serializer as MapLikeSerializer<Any?, Any?, T, *>)
-        val mapEntrySerial = kotlinx.serialization.builtins.MapEntrySerializer(casted.keySerializer, casted.valueSerializer)
+        val mapEntrySerial = MapEntrySerializer(casted.keySerializer, casted.valueSerializer)
         SetSerializer(mapEntrySerial).serialize(this, (value as Map<*, *>).entries)
     }
 }
@@ -173,6 +181,78 @@
     }
 }
 
+/**
+ * When writing a one-of element with polymorphic serializer,
+ * use [OneOfPolymorphicEncoder] to skip the first element of type name,
+ * and then dispatch to [OneOfElementEncoder] when calling [beginStructure]
+ * to write the content value, with ProtoNumber overridden by class annotation,
+ * directly back to the output stream.
+ */
+private class OneOfPolymorphicEncoder(
+    proto: ProtoBuf,
+    private val parentWriter: ProtobufWriter,
+    descriptor: SerialDescriptor
+) : ProtobufEncoder(proto, parentWriter, descriptor) {
+
+    init {
+        require(descriptor.kind is PolymorphicKind) {
+            "The serializer of one of type ${descriptor.serialName} should be using generic polymorphic serializer, but got ${descriptor.kind}."
+        }
+    }
+
+    override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder {
+        return if (descriptor == this.descriptor) {
+            this
+        } else {
+            OneOfElementEncoder(
+                proto = proto,
+                parentWriter = parentWriter,
+                descriptor = descriptor
+            )
+        }
+    }
+
+    override fun encodeInline(descriptor: SerialDescriptor): Encoder {
+        return encodeTaggedInline(popTag().overrideId(descriptor.extractParameters(0).protoId), descriptor)
+    }
+
+    override fun encodeTaggedString(tag: ProtoDesc, value: String) {
+        // the first element with type string is the discriminator of polymorphic serializer with class name
+        // just ignore it
+        if (tag != POLYMORPHIC_NAME_TAG) {
+            super.encodeTaggedString(tag, value)
+        }
+    }
+
+    override fun SerialDescriptor.getTag(index: Int) = when (index) {
+        // 0 for discriminator
+        0 -> POLYMORPHIC_NAME_TAG
+        1 -> extractParameters(index)
+        else -> throw SerializationException("Unsupported index: $index in a oneOf type $serialName, which should be using generic polymorphic serializer")
+    }
+}
+
+/**
+ * A helper encoder for one-of element to write the content value,
+ * with ProtoNumber overridden by class annotation,
+ * directly back to the output stream.
+ */
+private class OneOfElementEncoder(
+    proto: ProtoBuf,
+    parentWriter: ProtobufWriter,
+    descriptor: SerialDescriptor
+) : ProtobufEncoder(proto, parentWriter, descriptor) {
+    init {
+        require(descriptor.elementsCount == 1) {
+            "Implementation of oneOf type ${descriptor.serialName} should contain only 1 element, but get ${descriptor.elementsCount}"
+        }
+        val protoNumber = descriptor.getElementAnnotations(0).filterIsInstance<ProtoNumber>().singleOrNull()
+        require(protoNumber != null) {
+            "Implementation of oneOf type ${descriptor.serialName} should have @ProtoNumber annotation"
+        }
+    }
+}
+
 private class MapRepeatedEncoder(
     proto: ProtoBuf,
     parentTag: ProtoDesc,
diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufReader.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufReader.kt
index c7d4ea0..5b8ce1c 100644
--- a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufReader.kt
+++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufReader.kt
@@ -13,7 +13,7 @@
     @JvmField
     public var currentId = -1
     @JvmField
-    public var currentType = -1
+    public var currentType = ProtoWireType.INVALID
     private var pushBack = false
     private var pushBackHeader = 0
 
@@ -23,13 +23,13 @@
     public fun readTag(): Int {
         if (pushBack) {
             pushBack = false
-            val previousHeader = (currentId shl 3) or currentType
+            val previousHeader = (currentId shl 3) or currentType.typeId
             return updateIdAndType(pushBackHeader).also {
                 pushBackHeader = previousHeader
             }
         }
         // Header to use when pushed back is the old id/type
-        pushBackHeader = (currentId shl 3) or currentType
+        pushBackHeader = (currentId shl 3) or currentType.typeId
 
         val header = input.readVarint64(true).toInt()
         return updateIdAndType(header)
@@ -38,11 +38,11 @@
     private fun updateIdAndType(header: Int): Int {
         return if (header == -1) {
             currentId = -1
-            currentType = -1
+            currentType = ProtoWireType.INVALID
             -1
         } else {
             currentId = header ushr 3
-            currentType = header and 0b111
+            currentType = ProtoWireType.from(header and 0b111)
             currentId
         }
     }
@@ -50,31 +50,38 @@
     public fun pushBackTag() {
         pushBack = true
 
-        val nextHeader = (currentId shl 3) or currentType
+        val nextHeader = (currentId shl 3) or currentType.typeId
         updateIdAndType(pushBackHeader)
         pushBackHeader = nextHeader
     }
 
     fun skipElement() {
         when (currentType) {
-            VARINT -> readInt(ProtoIntegerType.DEFAULT)
-            i64 -> readLong(ProtoIntegerType.FIXED)
-            SIZE_DELIMITED -> readByteArray()
-            i32 -> readInt(ProtoIntegerType.FIXED)
+            ProtoWireType.VARINT -> readInt(ProtoIntegerType.DEFAULT)
+            ProtoWireType.i64 -> readLong(ProtoIntegerType.FIXED)
+            ProtoWireType.SIZE_DELIMITED -> skipSizeDelimited()
+            ProtoWireType.i32 -> readInt(ProtoIntegerType.FIXED)
             else -> throw ProtobufDecodingException("Unsupported start group or end group wire type: $currentType")
         }
     }
 
     @Suppress("NOTHING_TO_INLINE")
-    private inline fun assertWireType(expected: Int) {
+    private inline fun assertWireType(expected: ProtoWireType) {
         if (currentType != expected) throw ProtobufDecodingException("Expected wire type $expected, but found $currentType")
     }
 
     fun readByteArray(): ByteArray {
-        assertWireType(SIZE_DELIMITED)
+        assertWireType(ProtoWireType.SIZE_DELIMITED)
         return readByteArrayNoTag()
     }
 
+    fun skipSizeDelimited() {
+        assertWireType(ProtoWireType.SIZE_DELIMITED)
+        val length = decode32()
+        checkLength(length)
+        input.skipExactNBytes(length)
+    }
+
     fun readByteArrayNoTag(): ByteArray {
         val length = decode32()
         checkLength(length)
@@ -82,7 +89,7 @@
     }
 
     fun objectInput(): ByteArrayInput {
-        assertWireType(SIZE_DELIMITED)
+        assertWireType(ProtoWireType.SIZE_DELIMITED)
         return objectTaglessInput()
     }
 
@@ -93,7 +100,7 @@
     }
 
     fun readInt(format: ProtoIntegerType): Int {
-        val wireType = if (format == ProtoIntegerType.FIXED) i32 else VARINT
+        val wireType = if (format == ProtoIntegerType.FIXED) ProtoWireType.i32 else ProtoWireType.VARINT
         assertWireType(wireType)
         return decode32(format)
     }
@@ -101,7 +108,7 @@
     fun readInt32NoTag(): Int = decode32()
 
     fun readLong(format: ProtoIntegerType): Long {
-        val wireType = if (format == ProtoIntegerType.FIXED) i64 else VARINT
+        val wireType = if (format == ProtoIntegerType.FIXED) ProtoWireType.i64 else ProtoWireType.VARINT
         assertWireType(wireType)
         return decode64(format)
     }
@@ -109,7 +116,7 @@
     fun readLongNoTag(): Long = decode64(ProtoIntegerType.DEFAULT)
 
     fun readFloat(): Float {
-        assertWireType(i32)
+        assertWireType(ProtoWireType.i32)
         return Float.fromBits(readIntLittleEndian())
     }
 
@@ -136,7 +143,7 @@
     }
 
     fun readDouble(): Double {
-        assertWireType(i64)
+        assertWireType(ProtoWireType.i64)
         return Double.fromBits(readLongLittleEndian())
     }
 
@@ -145,7 +152,7 @@
     }
 
     fun readString(): String {
-        assertWireType(SIZE_DELIMITED)
+        assertWireType(ProtoWireType.SIZE_DELIMITED)
         val length = decode32()
         checkLength(length)
         return input.readString(length)
diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufTaggedBase.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufTaggedBase.kt
index 5c01643..ffa9f47 100644
--- a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufTaggedBase.kt
+++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufTaggedBase.kt
@@ -14,6 +14,15 @@
  */
 internal const val MISSING_TAG = 19_500L
 
+/**
+ * Tag indicating that now is handling the first element of polymorphic serializer,
+ * which is the serial name that should match the class name.
+ *
+ * In oneof element, such element should be ignored.
+ */
+internal const val POLYMORPHIC_NAME_TAG: ProtoDesc = 19501
+
+
 internal abstract class ProtobufTaggedBase {
     private var tagsStack = LongArray(8)
     @JvmField
diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufTaggedEncoder.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufTaggedEncoder.kt
index 84e5839..d061e40 100644
--- a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufTaggedEncoder.kt
+++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufTaggedEncoder.kt
@@ -123,13 +123,27 @@
     public final override fun encodeStringElement(descriptor: SerialDescriptor, index: Int, value: String): Unit =
         encodeTaggedString(descriptor.getTag(index), value)
 
+    private fun SerialKind.isMapOrList() =
+        this == StructureKind.MAP || this == StructureKind.LIST
+
     public final override fun <T : Any?> encodeSerializableElement(
         descriptor: SerialDescriptor,
         index: Int,
         serializer: SerializationStrategy<T>,
         value: T
     ) {
-        nullableMode = NullableMode.NOT_NULL
+        nullableMode =
+            if (descriptor.isElementOptional(index))
+                NullableMode.OPTIONAL
+            else {
+                val elementDescriptor = descriptor.getElementDescriptor(index)
+                if (elementDescriptor.kind.isMapOrList())
+                    NullableMode.COLLECTION
+                else if (!descriptor.kind.isMapOrList() && elementDescriptor.isNullable) // or: `serializer.descriptor`
+                    NullableMode.ACCEPTABLE
+                else
+                    NullableMode.NOT_NULL
+            }
 
         pushTag(descriptor.getTag(index))
         encodeSerializableValue(serializer, value)
@@ -141,14 +155,12 @@
         serializer: SerializationStrategy<T>,
         value: T?
     ) {
-        val elementKind = descriptor.getElementDescriptor(index).kind
-        nullableMode = if (descriptor.isElementOptional(index)) {
+        nullableMode = if (descriptor.isElementOptional(index))
             NullableMode.OPTIONAL
-        } else if (elementKind == StructureKind.MAP || elementKind == StructureKind.LIST) {
+        else if (descriptor.getElementDescriptor(index).kind.isMapOrList())
             NullableMode.COLLECTION
-        } else {
+        else
             NullableMode.ACCEPTABLE
-        }
 
         pushTag(descriptor.getTag(index))
         encodeNullableSerializableValue(serializer, value)
diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufWriter.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufWriter.kt
index ba16427..f432112 100644
--- a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufWriter.kt
+++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/ProtobufWriter.kt
@@ -10,7 +10,7 @@
 
 internal class ProtobufWriter(private val out: ByteArrayOutput) {
     fun writeBytes(bytes: ByteArray, tag: Int) {
-        out.encode32((tag shl 3) or SIZE_DELIMITED)
+        out.encode32(ProtoWireType.SIZE_DELIMITED.wireIntWithTag(tag))
         writeBytes(bytes)
     }
 
@@ -20,7 +20,7 @@
     }
 
     fun writeOutput(output: ByteArrayOutput, tag: Int) {
-        out.encode32((tag shl 3) or SIZE_DELIMITED)
+        out.encode32(ProtoWireType.SIZE_DELIMITED.wireIntWithTag(tag))
         writeOutput(output)
     }
 
@@ -30,8 +30,8 @@
     }
 
     fun writeInt(value: Int, tag: Int, format: ProtoIntegerType) {
-        val wireType = if (format == ProtoIntegerType.FIXED) i32 else VARINT
-        out.encode32((tag shl 3) or wireType)
+        val wireType = if (format == ProtoIntegerType.FIXED) ProtoWireType.i32 else ProtoWireType.VARINT
+        out.encode32(wireType.wireIntWithTag(tag))
         out.encode32(value, format)
     }
 
@@ -40,8 +40,8 @@
     }
 
     fun writeLong(value: Long, tag: Int, format: ProtoIntegerType) {
-        val wireType = if (format == ProtoIntegerType.FIXED) i64 else VARINT
-        out.encode32((tag shl 3) or wireType)
+        val wireType = if (format == ProtoIntegerType.FIXED) ProtoWireType.i64 else ProtoWireType.VARINT
+        out.encode32(wireType.wireIntWithTag(tag))
         out.encode64(value, format)
     }
 
@@ -60,7 +60,7 @@
     }
 
     fun writeDouble(value: Double, tag: Int) {
-        out.encode32((tag shl 3) or i64)
+        out.encode32(ProtoWireType.i64.wireIntWithTag(tag))
         out.writeLong(value.reverseBytes())
     }
 
@@ -69,7 +69,7 @@
     }
 
     fun writeFloat(value: Float, tag: Int) {
-        out.encode32((tag shl 3) or i32)
+        out.encode32(ProtoWireType.i32.wireIntWithTag(tag))
         out.writeInt(value.reverseBytes())
     }
 
diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/Streams.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/Streams.kt
index 575c5e7..ea2ab5e 100644
--- a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/Streams.kt
+++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/internal/Streams.kt
@@ -33,6 +33,12 @@
         return b
     }
 
+
+    fun skipExactNBytes(bytesCount: Int) {
+        ensureEnoughBytes(bytesCount)
+        position += bytesCount
+    }
+
     private fun ensureEnoughBytes(bytesCount: Int) {
         if (bytesCount > availableBytes) {
             throw SerializationException("Unexpected EOF, available $availableBytes bytes, requested: $bytesCount")
diff --git a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/schema/ProtoBufSchemaGenerator.kt b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/schema/ProtoBufSchemaGenerator.kt
index 4f4ca9c..e4826bb 100644
--- a/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/schema/ProtoBufSchemaGenerator.kt
+++ b/formats/protobuf/commonMain/src/kotlinx/serialization/protobuf/schema/ProtoBufSchemaGenerator.kt
@@ -167,55 +167,109 @@
 
         val usedNumbers: MutableSet<Int> = mutableSetOf()
         val nestedTypes = mutableListOf<TypeDefinition>()
-        for (index in 0 until messageDescriptor.elementsCount) {
-            val fieldName = messageDescriptor.getElementName(index)
-            fieldName.checkIsValidIdentifier {
-                "Invalid name of the field '$fieldName' in message '$messageName' for class with serial " +
-                        "name '${messageDescriptor.serialName}'"
-            }
-
-            val fieldDescriptor = messageDescriptor.getElementDescriptor(index)
-
-            val isList = fieldDescriptor.isProtobufRepeated
-
-            nestedTypes += when {
-                fieldDescriptor.isProtobufNamedType -> generateNamedType(messageType, index)
-                isList -> generateListType(messageType, index)
-                fieldDescriptor.isProtobufMap -> generateMapType(messageType, index)
-                else -> throw IllegalStateException(
-                    "Unprocessed message field type with serial name " +
-                            "'${fieldDescriptor.serialName}' and kind '${fieldDescriptor.kind}'"
-                )
-            }
-
-
-            val annotations = messageDescriptor.getElementAnnotations(index)
-            val number = annotations.filterIsInstance<ProtoNumber>().singleOrNull()?.number ?: (index + 1)
-            if (!usedNumbers.add(number)) {
-                throw IllegalArgumentException("Field number $number is repeated in the class with serial name ${messageDescriptor.serialName}")
-            }
-
-            append(' ').append(fieldName).append(" = ").append(number)
-
-            val isPackRequested = annotations.filterIsInstance<ProtoPacked>().singleOrNull() != null
-
-            when {
-                !isPackRequested ||
-                !isList || // ignore as packed only meaningful on repeated types
-                !fieldDescriptor.getElementDescriptor(0).isPackable // Ignore if the type is not allowed to be packed
-                     -> appendLine(';')
-                else -> appendLine(" [packed=true];")
-            }
-        }
+        generateMessageField(messageName, messageType, nestedTypes, usedNumbers)
         appendLine('}')
 
         return nestedTypes
     }
 
-    private fun StringBuilder.generateNamedType(messageType: TypeDefinition, index: Int): List<TypeDefinition> {
-        val messageDescriptor = messageType.descriptor
+    private fun StringBuilder.generateMessageField(
+        messageName: String,
+        parentType: TypeDefinition,
+        nestedTypes: MutableList<TypeDefinition>,
+        usedNumbers: MutableSet<Int>,
+        counts: Int = parentType.descriptor.elementsCount,
+        getAnnotations: (Int) -> List<Annotation> = { parentType.descriptor.getElementAnnotations(it) },
+        getChildType: (Int) -> TypeDefinition = { parentType.descriptor.getElementDescriptor(it).let(::TypeDefinition) },
+        getChildNumber: (Int) -> Int = { parentType.descriptor.getElementAnnotations(it).filterIsInstance<ProtoNumber>().singleOrNull()?.number ?: (it + 1) },
+        getChildName: (Int) -> String = { parentType.descriptor.getElementName(it) },
+        inOneOfStruct: Boolean = false,
+    ) {
+        val messageDescriptor = parentType.descriptor
+        for (index in 0 until counts) {
+            val fieldName = getChildName(index)
+            fieldName.checkIsValidIdentifier {
+                "Invalid name of the field '$fieldName' in ${if (inOneOfStruct) "oneof" else ""} message '$messageName' for class with serial " +
+                    "name '${messageDescriptor.serialName}'"
+            }
 
-        val fieldDescriptor = messageDescriptor.getElementDescriptor(index)
+            val fieldType = getChildType(index)
+            val fieldDescriptor = fieldType.descriptor
+
+            val number = getChildNumber(index)
+            if (messageDescriptor.isChildOneOfMessage(index)) {
+                require(!inOneOfStruct) {
+                    "Cannot have nested oneof in oneof struct: ${messageName}.$fieldName"
+                }
+                val subDescriptor = fieldDescriptor.getElementDescriptor(1).elementDescriptors.toList()
+                append("  ").append("oneof").append(' ').append(fieldName).appendLine(" {")
+                subDescriptor.forEach { desc ->
+                    require(desc.elementsCount == 1) {
+                        "Implementation of oneOf type ${desc.serialName} should contain only 1 element, but get ${desc.elementsCount}"
+                    }
+                    generateMessageField(
+                        messageName = messageName,
+                        parentType = TypeDefinition(desc),
+                        nestedTypes = nestedTypes,
+                        usedNumbers = usedNumbers,
+                        counts = desc.elementsCount,
+                        getAnnotations = { desc.annotations },
+                        getChildType = { desc.elementDescriptors.single().let(::TypeDefinition) },
+                        getChildNumber = { desc.getElementAnnotations(0).filterIsInstance<ProtoNumber>().singleOrNull()?.number ?: (it + 1) },
+                        getChildName = { desc.getElementName(0) },
+                        inOneOfStruct = true,
+                    )
+                }
+                appendLine("  }")
+            } else {
+                val annotations = getAnnotations(index)
+
+                val isList = fieldDescriptor.isProtobufRepeated
+
+                nestedTypes += when {
+                    fieldDescriptor.isProtobufNamedType -> generateNamedType(
+                        fieldDescriptor = messageDescriptor.getElementDescriptor(index),
+                        annotations = messageDescriptor.getElementAnnotations(index),
+                        isSealedPolymorphic = messageDescriptor.isSealedPolymorphic && index == 1,
+                        isOptional = messageDescriptor.isElementOptional(index),
+                        inOneOfStruct = inOneOfStruct,
+                        indent = if (inOneOfStruct) 2 else 1,
+                    )
+                    isList -> generateListType(parentType, index)
+                    fieldDescriptor.isProtobufMap -> generateMapType(parentType, index)
+                    else -> throw IllegalStateException(
+                        "Unprocessed message field type with serial name " +
+                            "'${fieldDescriptor.serialName}' and kind '${fieldDescriptor.kind}'"
+                    )
+                }
+                if (!usedNumbers.add(number)) {
+                    throw IllegalArgumentException("Field number $number is repeated in the class with serial name ${messageDescriptor.serialName}")
+                }
+
+                append(' ').append(fieldName).append(" = ").append(number)
+
+                val isPackRequested = annotations.filterIsInstance<ProtoPacked>().singleOrNull() != null
+
+                when {
+                    !isPackRequested ||
+                        !isList || // ignore as packed only meaningful on repeated types
+                        !fieldDescriptor.getElementDescriptor(0).isPackable // Ignore if the type is not allowed to be packed
+                    -> appendLine(';')
+
+                    else -> appendLine(" [packed=true];")
+                }
+            }
+        }
+    }
+
+    private fun StringBuilder.generateNamedType(
+        fieldDescriptor: SerialDescriptor,
+        annotations: List<Annotation>,
+        isSealedPolymorphic: Boolean,
+        isOptional: Boolean,
+        inOneOfStruct: Boolean = false,
+        indent: Int = 1,
+    ): List<TypeDefinition> {
         var unwrappedFieldDescriptor = fieldDescriptor
         while (unwrappedFieldDescriptor.isInline) {
             unwrappedFieldDescriptor = unwrappedFieldDescriptor.getElementDescriptor(0)
@@ -223,18 +277,18 @@
 
         val nestedTypes: List<TypeDefinition>
         val typeName: String = when {
-            messageDescriptor.isSealedPolymorphic && index == 1 -> {
-                appendLine("  // decoded as message with one of these types:")
+            isSealedPolymorphic -> {
+                append(" ".repeat(indent * 2)).appendLine("// decoded as message with one of these types:")
                 nestedTypes = unwrappedFieldDescriptor.elementDescriptors.map { TypeDefinition(it) }.toList()
                 nestedTypes.forEachIndexed { _, childType ->
-                    append("  //   message ").append(childType.descriptor.messageOrEnumName).append(", serial name '")
+                    append(" ".repeat(indent * 2)).append("//   message ").append(childType.descriptor.messageOrEnumName).append(", serial name '")
                         .append(removeLineBreaks(childType.descriptor.serialName)).appendLine('\'')
                 }
                 unwrappedFieldDescriptor.scalarTypeName()
             }
             unwrappedFieldDescriptor.isProtobufScalar -> {
                 nestedTypes = emptyList()
-                unwrappedFieldDescriptor.scalarTypeName(messageDescriptor.getElementAnnotations(index))
+                unwrappedFieldDescriptor.scalarTypeName(annotations)
             }
             unwrappedFieldDescriptor.isOpenPolymorphic -> {
                 nestedTypes = listOf(SyntheticPolymorphicType)
@@ -247,12 +301,18 @@
             }
         }
 
-        if (messageDescriptor.isElementOptional(index)) {
-            appendLine("  // WARNING: a default value decoded when value is missing")
+        if (isOptional) {
+            append(" ".repeat(indent * 2)).appendLine("// WARNING: a default value decoded when value is missing")
         }
-        val optional = fieldDescriptor.isNullable || messageDescriptor.isElementOptional(index)
+        val optional = fieldDescriptor.isNullable || isOptional
 
-        append("  ").append(if (optional) "optional " else "required ").append(typeName)
+        append(" ".repeat(indent * 2)).append(
+            when {
+                inOneOfStruct -> ""
+                optional -> "optional "
+                else -> "required "
+            }
+        ).append(typeName)
 
         return nestedTypes
     }
@@ -396,6 +456,9 @@
     private val SerialDescriptor.messageOrEnumName: String
         get() = (serialName.substringAfterLast('.', serialName)).removeSuffix("?")
 
+    private fun SerialDescriptor.isChildOneOfMessage(index: Int): Boolean =
+        this.getElementDescriptor(index).isSealedPolymorphic && this.getElementAnnotations(index).any { it is ProtoOneOf }
+
     private fun SerialDescriptor.protobufTypeName(annotations: List<Annotation> = emptyList()): String {
         return if (isProtobufScalar) {
             scalarTypeName(annotations)
diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/InvalidFieldNumberTest.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/InvalidFieldNumberTest.kt
new file mode 100644
index 0000000..81e01e4
--- /dev/null
+++ b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/InvalidFieldNumberTest.kt
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.protobuf
+
+
+import kotlinx.serialization.*
+import kotlin.test.*
+
+class InvalidFieldNumberTest {
+
+    @Serializable
+    data class Holder(val value: Int)
+
+    @Serializable
+    data class ListHolder(val value: List<Int>)
+
+    @Serializable
+    data class ZeroProtoNumber(@ProtoNumber(0) val value: Int)
+
+    @Serializable
+    data class NegativeProtoNumber(@ProtoNumber(-5) val value: Int)
+
+    @Test
+    fun testDeserializeZeroInput() {
+        assertFailsWithMessage<SerializationException>("0 is not allowed as the protobuf field number in kotlinx.serialization.protobuf.InvalidFieldNumberTest.Holder, the input bytes may have been corrupted") {
+            // first value with field number = 0
+            val hexString = "000f"
+            ProtoBuf.decodeFromHexString<Holder>(hexString)
+        }
+    }
+
+    @Test
+    fun testDeserializeZeroInputForElement() {
+        assertFailsWithMessage<SerializationException>("0 is not allowed as the protobuf field number in kotlinx.serialization.protobuf.InvalidFieldNumberTest.ListHolder, the input bytes may have been corrupted") {
+            // first element with field number = 0
+            val hexString = "000f"
+            ProtoBuf.decodeFromHexString<ListHolder>(hexString)
+        }
+    }
+
+    @Test
+    fun testSerializeZeroProtoNumber() {
+        assertFailsWithMessage<SerializationException>("0 is not allowed in ProtoNumber for property 'value' of 'kotlinx.serialization.protobuf.InvalidFieldNumberTest.ZeroProtoNumber', because protobuf supports field numbers in range 1..2147483647") {
+            ProtoBuf.encodeToHexString(ZeroProtoNumber(42))
+        }
+    }
+
+    @Test
+    fun testDeserializeZeroProtoNumber() {
+        assertFailsWithMessage<SerializationException>("0 is not allowed in ProtoNumber for property 'value' of 'kotlinx.serialization.protobuf.InvalidFieldNumberTest.ZeroProtoNumber', because protobuf supports field numbers in range 1..2147483647") {
+            ProtoBuf.decodeFromHexString<ZeroProtoNumber>("000f")
+        }
+    }
+
+    @Test
+    fun testSerializeNegativeProtoNumber() {
+        assertFailsWithMessage<SerializationException>("-5 is not allowed in ProtoNumber for property 'value' of 'kotlinx.serialization.protobuf.InvalidFieldNumberTest.NegativeProtoNumber', because protobuf supports field numbers in range 1..2147483647") {
+            ProtoBuf.encodeToHexString(NegativeProtoNumber(42))
+        }
+    }
+
+    @Test
+    fun testDeserializeNegativeProtoNumber() {
+        assertFailsWithMessage<SerializationException>("-5 is not allowed in ProtoNumber for property 'value' of 'kotlinx.serialization.protobuf.InvalidFieldNumberTest.NegativeProtoNumber', because protobuf supports field numbers in range 1..2147483647") {
+            ProtoBuf.decodeFromHexString<NegativeProtoNumber>("000f")
+        }
+    }
+}
diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/PackedArraySerializerTest.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/PackedArraySerializerTest.kt
index e7bf676..d6e604f 100644
--- a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/PackedArraySerializerTest.kt
+++ b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/PackedArraySerializerTest.kt
@@ -47,7 +47,6 @@
 
     @Serializable
     data class PackedStringCarrier(
-        @ProtoNumber(0)
         @ProtoPacked
         val s: List<String>
     )
@@ -110,12 +109,12 @@
     @Test
     fun testEncodeAnnotatedStringList() {
         val obj = PackedStringCarrier(listOf("aaa", "bbb", "ccc"))
-        val expectedHex = "020361616102036262620203636363"
+        val expectedHex = "0a036161610a036262620a03636363"
         val encodedHex = ProtoBuf.encodeToHexString(obj)
         assertEquals(expectedHex, encodedHex)
         assertEquals(obj, ProtoBuf.decodeFromHexString<PackedStringCarrier>(expectedHex))
 
-        val invalidPackedHex = "020C036161610362626203636363"
+        val invalidPackedHex = "0a0C036161610362626203636363"
         val decoded = ProtoBuf.decodeFromHexString<PackedStringCarrier>(invalidPackedHex)
         val invalidString = "\u0003aaa\u0003bbb\u0003ccc"
         assertEquals(PackedStringCarrier(listOf(invalidString)), decoded)
diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtoOneofInline.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtoOneofInline.kt
new file mode 100644
index 0000000..4e303b5
--- /dev/null
+++ b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtoOneofInline.kt
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.protobuf
+
+import kotlinx.serialization.*
+import kotlin.jvm.*
+import kotlin.test.*
+
+class ProtoInline {
+
+    @Serializable
+    data class OneOfDataNullable(
+        @ProtoOneOf val i: ITypeWithInlineClass?,
+        @ProtoNumber(3) val name: String
+    )
+
+    @Serializable
+    sealed interface ITypeWithInlineClass
+
+    @Serializable
+    @JvmInline
+    value class StringInlineType(@ProtoNumber(12) val s: String) : ITypeWithInlineClass
+
+    @Test
+    fun testOneOfStringTypeNullable() {
+        val dataString = OneOfDataNullable(
+            StringInlineType("bar"),
+            "foo")
+        ProtoBuf.encodeToHexString(OneOfDataNullable.serializer(), dataString).also {
+            /**
+             * 12: {"bar"}
+             * 3: {"foo"}
+             */
+            assertEquals("62036261721a03666f6f", it)
+        }
+        ProtoBuf.decodeFromHexString<OneOfDataNullable>("62036261721a03666f6f").also {
+            assertEquals(dataString, it)
+        }
+        val dataStringNull = OneOfDataNullable(null, "foo")
+        ProtoBuf.encodeToHexString(OneOfDataNullable.serializer(), dataStringNull).also {
+            /**
+             * 3: {"foo"}
+             */
+            assertEquals("1a03666f6f", it)
+        }
+    }
+}
\ No newline at end of file
diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtoTagExceptionTest.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtoTagExceptionTest.kt
new file mode 100644
index 0000000..a119ea5
--- /dev/null
+++ b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtoTagExceptionTest.kt
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.protobuf
+
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.decodeFromHexString
+import kotlinx.serialization.encodeToHexString
+import kotlinx.serialization.protobuf.internal.ProtobufDecodingException
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class ProtoTagExceptionTest {
+
+    @Serializable
+    data class TestDataToBuildWrongWireType(
+        @ProtoNumber(1) val a: Int,
+        @ProtoNumber(2) val b: Int,
+    )
+
+    @Serializable
+    data class TestData(
+        @ProtoNumber(1) val a: Int,
+        @ProtoNumber(2) val b: String,
+    )
+
+    @Test
+    fun testWrongTypeMessage() {
+        val build = ProtoBuf.encodeToHexString(TestDataToBuildWrongWireType(42, 42))
+
+        assertFailsWith<IllegalArgumentException>(
+            assertion = {
+                assertFailsWith(
+                    "Error while decoding kotlinx.serialization.protobuf.ProtoTagExceptionTest.TestData",
+                    "Error while decoding proto number 2 of kotlinx.serialization.protobuf.ProtoTagExceptionTest.TestData",
+                    "Expected wire type SIZE_DELIMITED(2), but found VARINT(0)",
+                )
+            }
+        ) {
+            ProtoBuf.decodeFromHexString<TestData>(build)
+        }
+    }
+
+    @Serializable
+    data class TestNestedDataToBuild(
+        @ProtoNumber(1) val nested: TestDataToBuildWrongWireType,
+        @ProtoNumber(2) val a: String,
+    )
+
+    @Serializable
+    data class TestNestedData(
+        @ProtoNumber(1) val nested: TestData,
+        @ProtoNumber(2) val a: String,
+    )
+
+    @Test
+    fun testWrongIntFieldInNestedMessage() {
+        val build = ProtoBuf.encodeToHexString(TestNestedDataToBuild(TestDataToBuildWrongWireType(42, 42), "foo"))
+
+        assertFailsWith<IllegalArgumentException>(
+            assertion = {
+                assertFailsWith(
+                    "Error while decoding kotlinx.serialization.protobuf.ProtoTagExceptionTest.TestNestedData",
+                    "Error while decoding kotlinx.serialization.protobuf.ProtoTagExceptionTest.TestData at proto number 1 of kotlinx.serialization.protobuf.ProtoTagExceptionTest.TestNestedData",
+                    "Error while decoding proto number 2 of kotlinx.serialization.protobuf.ProtoTagExceptionTest.TestData",
+                    "Expected wire type SIZE_DELIMITED(2), but found VARINT(0)",
+                )
+            }
+        ) {
+            ProtoBuf.decodeFromHexString<TestNestedData>(build)
+        }
+        assertFailsWith<IllegalArgumentException>(
+            assertion = {
+                assertFailsWith(
+                    "Error while decoding kotlinx.serialization.protobuf.ProtoTagExceptionTest.TestData",
+                    "Error while decoding proto number 1 of kotlinx.serialization.protobuf.ProtoTagExceptionTest.TestData",
+                    "Expected wire type VARINT(0), but found SIZE_DELIMITED(2)",
+                )
+            }
+        ) {
+            ProtoBuf.decodeFromHexString<TestData>(build)
+        }
+    }
+
+    @Test
+    fun testWrongStringFieldInNestedMessage() {
+        val build = ProtoBuf.encodeToHexString(TestNestedDataToBuild(TestDataToBuildWrongWireType(42, 42), "foo"))
+        assertFailsWith<IllegalArgumentException>(
+            assertion = {
+                assertFailsWith(
+                    "Error while decoding kotlinx.serialization.protobuf.ProtoTagExceptionTest.TestData",
+                    "Error while decoding proto number 1 of kotlinx.serialization.protobuf.ProtoTagExceptionTest.TestData",
+                    "Expected wire type VARINT(0), but found SIZE_DELIMITED(2)",
+                )
+            }
+        ) {
+            ProtoBuf.decodeFromHexString<TestData>(build)
+        }
+    }
+
+    @Serializable
+    data class TestDataWithMessageList(@ProtoNumber(1) @ProtoPacked val list: List<TestData>)
+
+    @Serializable
+    data class TestDataWithWrongList(@ProtoNumber(1) @ProtoPacked val list: List<TestDataToBuildWrongWireType>)
+
+    @Test
+    fun testWrongIntFieldInNestedMessageInList() {
+        val build = ProtoBuf.encodeToHexString(TestDataWithWrongList(listOf(TestDataToBuildWrongWireType(42, 42))))
+        assertFailsWith<ProtobufDecodingException>(
+            assertion = {
+                assertFailsWith("Error while decoding kotlinx.serialization.protobuf.ProtoTagExceptionTest.TestDataWithMessageList")
+                assertCausedBy<ProtobufDecodingException> {
+                    assertFailsWith("Error while decoding kotlin.collections.ArrayList at proto number 1 of kotlinx.serialization.protobuf.ProtoTagExceptionTest.TestDataWithMessageList")
+                    assertCausedBy<ProtobufDecodingException> {
+                        assertFailsWith(
+                            "Error while decoding index 0 in repeated field of kotlinx.serialization.protobuf.ProtoTagExceptionTest.TestData",
+                            "Error while decoding proto number 2 of kotlinx.serialization.protobuf.ProtoTagExceptionTest.TestData",
+                            "Expected wire type SIZE_DELIMITED(2), but found VARINT(0)",
+                        )
+                    }
+                }
+            }
+        ) {
+            val result = ProtoBuf.decodeFromHexString<TestDataWithMessageList>(build)
+        }
+    }
+
+    @Serializable
+    data class TestDataWithMessageMapValue(@ProtoNumber(1) val map: Map<String, TestData>)
+
+    @Serializable
+    data class TestDataWithWrongMapValue(@ProtoNumber(1) val map: Map<String, TestDataToBuildWrongWireType>)
+
+    @Test
+    fun testWrongIntFieldInNestedMapValue() {
+        val build = ProtoBuf.encodeToHexString(TestDataWithWrongMapValue(map = mapOf("1" to TestDataToBuildWrongWireType(42, 42))))
+        assertFailsWith<ProtobufDecodingException>(
+            assertion = {
+                assertFailsWith("Error while decoding kotlinx.serialization.protobuf.ProtoTagExceptionTest.TestDataWithMessageMapValue")
+                assertCausedBy<ProtobufDecodingException> {
+                    assertFailsWith("Error while decoding kotlin.collections.LinkedHashMap at proto number 1 of kotlinx.serialization.protobuf.ProtoTagExceptionTest.TestDataWithMessageMapValue")
+                    assertCausedBy<ProtobufDecodingException> {
+                        assertFailsWith(
+                            "Error while decoding kotlin.collections.Map.Entry at proto number 1 of kotlin.collections.LinkedHashSet",
+                            "Error while decoding value of index 0 in map field of kotlinx.serialization.protobuf.ProtoTagExceptionTest.TestData",
+                            "Error while decoding proto number 2 of kotlinx.serialization.protobuf.ProtoTagExceptionTest.TestData",
+                            "Expected wire type SIZE_DELIMITED(2), but found VARINT(0)",
+                        )
+                    }
+                }
+            }
+        ) {
+            ProtoBuf.decodeFromHexString<TestDataWithMessageMapValue>(build)
+        }
+    }
+
+
+    @Serializable
+    data class DuplicatingIdData(
+        @ProtoOneOf val bad: IDuplicatingIdType,
+        @ProtoNumber(3) val d: Int,
+    )
+
+    @Serializable
+    sealed interface IDuplicatingIdType
+
+    @Serializable
+    data class DuplicatingIdStringType(@ProtoNumber(3) val s: String) : IDuplicatingIdType
+
+    @Test
+    fun testDuplicatedIdClass() {
+        val duplicated = DuplicatingIdData(DuplicatingIdStringType("foo"), 42)
+        // Fine to encode duplicated proto number properties in wire data
+        ProtoBuf.encodeToHexString(duplicated).also {
+            /**
+             * 3:LEN {"foo"}
+             * 3:VARINT 42
+             */
+            assertEquals("1a03666f6f182a", it)
+        }
+
+        assertFailsWith<IllegalArgumentException>(
+            assertion = {
+                assertFailsWith(
+                    "Error while decoding kotlinx.serialization.protobuf.ProtoTagExceptionTest.DuplicatingIdData",
+                    "Error while decoding proto number 3 of kotlinx.serialization.protobuf.ProtoTagExceptionTest.DuplicatingIdData",
+                    "Expected wire type VARINT(0), but found SIZE_DELIMITED(2)",
+                )
+            }
+        ) {
+            /**
+             * 3:LEN {"foo"}
+             * 3:VARINT 42
+             */
+            ProtoBuf.decodeFromHexString<DuplicatingIdData>("1a03666f6f182a")
+        }
+    }
+}
\ No newline at end of file
diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufNothingTest.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufNothingTest.kt
index e90ff2b..bdc93c2 100644
--- a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufNothingTest.kt
+++ b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufNothingTest.kt
@@ -5,7 +5,6 @@
 package kotlinx.serialization.protobuf
 
 import kotlinx.serialization.*
-import kotlinx.serialization.test.*
 import kotlin.test.*
 
 class ProtobufNothingTest {
@@ -13,17 +12,16 @@
     /*private*/ data class NullableNothingBox(val value: Nothing?) // `private` doesn't work on the JS legacy target
 
     @Serializable
-    private data class ParameterizedBox<T : Any>(val value: T?)
+    private data class NullablePropertyNotNullUpperBoundParameterizedBox<T : Any>(val value: T?)
 
-    private inline fun <reified T> testConversion(data: T, expectedHexString: String) {
-        val string = ProtoBuf.encodeToHexString(data).uppercase()
-        assertEquals(expectedHexString, string)
-        assertEquals(data, ProtoBuf.decodeFromHexString(string))
-    }
+    @Serializable
+    private data class NullableUpperBoundParameterizedBox<T : Any?>(val value: T)
+
 
     @Test
     fun testNothing() {
         testConversion(NullableNothingBox(null), "")
-        testConversion(ParameterizedBox(null), "")
+        testConversion(NullablePropertyNotNullUpperBoundParameterizedBox(null), "")
+        testConversion(NullableUpperBoundParameterizedBox(null), "")
     }
 }
diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufOneOfTest.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufOneOfTest.kt
new file mode 100644
index 0000000..e272738
--- /dev/null
+++ b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufOneOfTest.kt
@@ -0,0 +1,719 @@
+/*
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.protobuf
+
+import kotlinx.serialization.*
+import kotlinx.serialization.builtins.*
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.modules.*
+import kotlinx.serialization.protobuf.internal.*
+import kotlin.jvm.*
+import kotlin.test.*
+
+class ProtobufOneOfTest {
+    @Serializable
+    data class OneOfData(
+        @ProtoOneOf val i: IType,
+        @ProtoNumber(3) val name: String
+    )
+
+    @Serializable
+    data class OneOfDataNullable(
+        @ProtoOneOf val i: IType?,
+        @ProtoNumber(3) val name: String
+    )
+
+    @Serializable
+    data class DataNoOneOf(
+        @ProtoNumber(5) val i: Int = 0,
+        @ProtoNumber(6) val s: String = "",
+        @ProtoNumber(3) val name: String,
+    )
+
+    @Serializable
+    sealed interface IType
+
+    @Serializable
+    data class IntType(@ProtoNumber(5) val i: Int = 0) : IType
+
+    @Serializable
+    @JvmInline
+    value class StringType(@ProtoNumber(6) val s: String) : IType
+
+    @Serializable
+    data class NestedIntType(@ProtoNumber(7) val intType: IntType) : IType
+
+    @Test
+    fun testOneOfIntType() {
+        val dataInt = OneOfData(IntType(42), "foo")
+        val intString = ProtoBuf.encodeToHexString(OneOfData.serializer(), dataInt)
+        /**
+         * 5:VARINT 42
+         * 3:LEN {"foo"}
+         */
+        assertEquals("282a1a03666f6f", intString)
+    }
+
+    @Test
+    fun testOneOfStringType() {
+        val dataString = OneOfData(StringType("bar"), "foo")
+        val stringString = ProtoBuf.encodeToHexString(OneOfData.serializer(), dataString)
+        /**
+         * 6:LEN {"bar"}
+         * 3:LEN {"foo"}
+         */
+        assertEquals("32036261721a03666f6f", stringString)
+    }
+
+    @Test
+    fun testOneOfDecodeStringType() {
+        /**
+         * 6:LEN {"bar"}
+         * 3:LEN {"foo"}
+         */
+        val dataString = ProtoBuf.decodeFromHexString<OneOfData>("32036261721a03666f6f")
+        assertEquals(OneOfData(StringType("bar"), "foo"), dataString)
+    }
+
+    @Test
+    fun testOneOfDecodeIntType() {
+        /**
+         * 5:VARINT 42
+         * 3:LEN {"foo"}
+         */
+        val dataInt = ProtoBuf.decodeFromHexString<OneOfData>("282a1a03666f6f")
+        assertEquals(OneOfData(IntType(42), "foo"), dataInt)
+    }
+
+    @Test
+    fun testOneOfIntTypeNullable() {
+        val dataInt = OneOfDataNullable(IntType(42), "foo")
+        ProtoBuf.encodeToHexString(OneOfDataNullable.serializer(), dataInt).also {
+            /**
+             * 5:VARINT 42
+             * 3:LEN {"foo"}
+             */
+            assertEquals("282a1a03666f6f", it)
+        }
+
+    }
+
+    @Test
+    fun testOneOfStringTypeNullable() {
+        val dataString = OneOfDataNullable(StringType("bar"), "foo")
+        ProtoBuf.encodeToHexString(OneOfDataNullable.serializer(), dataString).also {
+            /**
+             * 6:LEN {"bar"}
+             * 3:LEN {"foo"}
+             */
+            assertEquals("32036261721a03666f6f", it)
+        }
+        val dataStringNull = OneOfDataNullable(null, "foo")
+        ProtoBuf.encodeToHexString(OneOfDataNullable.serializer(), dataStringNull).also {
+            /**
+             * 3:LEN {"foo"}
+             */
+            assertEquals("1a03666f6f", it)
+        }
+    }
+
+    @Test
+    fun testOneOfDecodeNullable() {
+        /**
+         * 6:LEN {"bar"}
+         * 3:LEN {"foo"}
+         */
+        ProtoBuf.decodeFromHexString<OneOfDataNullable>("32036261721a03666f6f").let {
+            assertEquals(OneOfDataNullable(StringType("bar"), "foo"), it)
+        }
+        /**
+         * 5:VARINT 42
+         * 3:LEN {"foo"}
+         */
+        ProtoBuf.decodeFromHexString<OneOfDataNullable>("282a1a03666f6f").let {
+            assertEquals(OneOfDataNullable(IntType(42), "foo"), it)
+        }
+        /**
+         * 3:LEN {"foo"}
+         */
+        ProtoBuf.decodeFromHexString<OneOfDataNullable>("1a03666f6f").let {
+            assertEquals(OneOfDataNullable(null, "foo"), it)
+        }
+    }
+
+    @Test
+    fun testOneOfToFlatInt() {
+        val dataInt = OneOfData(IntType(42), "foo")
+        ProtoBuf.encodeToByteArray(dataInt).let {
+            ProtoBuf.decodeFromByteArray<DataNoOneOf>(it).let { data ->
+                assertEquals(
+                    DataNoOneOf(42, "", "foo"), data
+                )
+            }
+        }
+    }
+
+    @Test
+    fun testOneOfToFlatString() {
+        val dataString = OneOfData(StringType("bar"), "foo")
+        ProtoBuf.encodeToByteArray(dataString).let {
+            ProtoBuf.decodeFromByteArray<DataNoOneOf>(it).let { data ->
+                assertEquals(
+                    DataNoOneOf(0, "bar", "foo"), data
+                )
+            }
+        }
+    }
+
+    @Test
+    fun testFlatIntToOneOf() {
+        val dataInt = DataNoOneOf(42, "", "foo")
+        ProtoBuf.encodeToByteArray(dataInt).let {
+            ProtoBuf.decodeFromByteArray<OneOfData>(it).let { data ->
+                assertEquals(
+                    OneOfData(IntType(42), "foo"), data
+                )
+            }
+        }
+    }
+
+    @Test
+    fun testFlatStringToOneOf() {
+        val dataString = DataNoOneOf(0, "bar", "foo")
+        ProtoBuf.encodeToByteArray(dataString).let {
+            ProtoBuf.decodeFromByteArray<OneOfData>(it).let { data ->
+                assertEquals(
+                    OneOfData(StringType("bar"), "foo"), data
+                )
+            }
+        }
+    }
+
+    @Test
+    fun testEncodeNestedStruct() {
+        val data = OneOfData(NestedIntType(IntType(32)), "foo")
+        ProtoBuf.encodeToHexString(OneOfData.serializer(), data).also {
+            /**
+             * 7:LEN {5:VARINT 32}
+             * 3:LEN {"foo"}
+             */
+            assertEquals("3a0228201a03666f6f", it)
+        }
+    }
+
+    @Test
+    fun testDecodeNestedStruct() {
+        val data = OneOfData(NestedIntType(IntType(32)), "foo")
+        /**
+         * 7:LEN {5:VARINT 32}
+         * 3:LEN {"foo"}
+         */
+        ProtoBuf.decodeFromHexString<OneOfData>("3a0228201a03666f6f").also {
+            assertEquals(data, it)
+        }
+    }
+
+    @Serializable
+    data class FailOuter(@ProtoOneOf val i: IFailSuper, @ProtoNumber(3) val name: String)
+
+    @Serializable
+    data class FailOuterHelper(
+        @ProtoNumber(5) val i: Int,
+        @ProtoNumber(6) val j: Int,
+        @ProtoNumber(3) val name: String
+    )
+
+    @Serializable
+    sealed interface IFailSuper
+
+    @Serializable
+    data class FailType(@ProtoNumber(5) val i: Int, @ProtoNumber(6) val j: Int) : IFailSuper
+
+    @Test
+    fun testOneOfElementCheck() {
+        val data = FailOuter(FailType(1, 2), "foo")
+        assertFailsWithMessage<IllegalArgumentException>(
+            message = "Implementation of oneOf type" +
+                " kotlinx.serialization.protobuf.ProtobufOneOfTest.FailType" +
+                " should contain only 1 element, but get 2"
+        ) {
+            ProtoBuf.encodeToHexString(data)
+        }
+
+        /**
+         * 5:VARINT 42
+         * 3:LEN {"foo"}
+         */
+        assertFailsWithMessage<IllegalArgumentException>(
+            message = "Implementation of oneOf type" +
+                " kotlinx.serialization.protobuf.ProtobufOneOfTest.FailType" +
+                " should contain only 1 element, but get 2"
+        ) {
+            ProtoBuf.decodeFromHexString<FailOuter>("282a1a03666f6f")
+        }
+
+        /**
+         * 5:VARINT 1
+         * 6:VARINT 2
+         * 3:LEN {"foo"}
+         */
+        assertFailsWithMessage<IllegalArgumentException>(
+            message = "Implementation of oneOf type" +
+                " kotlinx.serialization.protobuf.ProtobufOneOfTest.FailType" +
+                " should contain only 1 element, but get 2"
+        ) {
+            ProtoBuf.decodeFromHexString<FailOuter>(
+                ProtoBuf.encodeToHexString(
+                    FailOuterHelper(
+                        i = 1,
+                        j = 2,
+                        name = "foo"
+                    )
+                )
+            )
+        }
+
+    }
+
+    @Serializable
+    data class NestedOneOfType(@ProtoNumber(9) val i: InnerNested) : IType
+
+    @Serializable
+    data class InnerNested(@ProtoOneOf val i: InnerOneOf)
+
+    @Serializable
+    sealed interface InnerOneOf
+
+    @Serializable
+    data class InnerInt(@ProtoNumber(1) val i: Int) : InnerOneOf
+
+    @Serializable
+    data class InnerString(@ProtoNumber(2) val s: String) : InnerOneOf
+
+    @Test
+    fun testEncodeNestedOneOf() {
+        val data = OneOfData(NestedOneOfType(i = InnerNested(InnerInt(32))), "foo")
+        ProtoBuf.encodeToHexString(OneOfData.serializer(), data).also {
+            /**
+             * 9:LEN {1:VARINT 32}
+             * 3:LEN {"foo"}
+             */
+            assertEquals("4a0208201a03666f6f", it)
+        }
+    }
+
+    @Test
+    fun testDecodeNestedOneOf() {
+        val data = OneOfData(NestedOneOfType(i = InnerNested(InnerInt(32))), "foo")
+        /**
+         * 9:LEN {1:VARINT 32}
+         * 3:LEN {"foo"}
+         */
+        ProtoBuf.decodeFromHexString<OneOfData>("4a0208201a03666f6f").also {
+            /**
+             * 9: {1: 32}
+             * 3: {"foo"}
+             */
+            assertEquals(data, it)
+        }
+    }
+
+    @Serializable
+    data class DoubleOneOfElement(
+        @ProtoOneOf val one: IType,
+        @ProtoNumber(3) val name: String,
+        @ProtoOneOf val two: OtherType
+    )
+
+    interface OtherType
+
+    @Serializable
+    data class OtherIntType(@ProtoNumber(11) val i: Int) : OtherType
+
+    @Serializable
+    data class OtherStringType(@ProtoNumber(12) val s: String) : OtherType
+
+    @Test
+    fun testEncodeDoubleOneOf() {
+        val module = SerializersModule {
+            polymorphic(OtherType::class) {
+                subclass(OtherStringType::class)
+            }
+        }
+        val buf = ProtoBuf {
+            serializersModule = module
+        }
+        val data = DoubleOneOfElement(
+            IntType(32),
+            "foo",
+            OtherStringType("bar")
+        )
+        buf.encodeToHexString(DoubleOneOfElement.serializer(), data).also {
+            /**
+             * 5:VARINT 32
+             * 3:LEN {"foo"}
+             * 12:LEN {"bar"}
+             */
+            assertEquals("28201a03666f6f6203626172", it)
+        }
+
+        assertFailsWithMessage<SerializationException>(
+            message = "Serializer for subclass 'OtherIntType' is not found in the polymorphic scope of 'OtherType'.\n" +
+                "Check if class with serial name 'OtherIntType' exists and serializer is registered in a corresponding SerializersModule.\n" +
+                "To be registered automatically, class 'OtherIntType' has to be '@Serializable', and the base class 'OtherType' has to be sealed and '@Serializable'."
+        ) {
+            buf.encodeToHexString(
+                DoubleOneOfElement.serializer(), DoubleOneOfElement(
+                    IntType(32),
+                    "foo",
+                    OtherIntType(32)
+                )
+            )
+        }
+    }
+
+    @Test
+    fun testDecodeDoubleOneOf() {
+        val module = SerializersModule {
+            polymorphic(OtherType::class) {
+                subclass(OtherStringType::class)
+            }
+        }
+        val buf = ProtoBuf {
+            serializersModule = module
+        }
+        val data = DoubleOneOfElement(
+            IntType(32),
+            "foo",
+            OtherStringType("bar")
+        )
+        /**
+         * 5:VARINT 32
+         * 3:LEN {"foo"}
+         * 12:LEN {"bar"}
+         */
+        buf.decodeFromHexString<DoubleOneOfElement>("28201a03666f6f6203626172").also {
+            assertEquals(data, it)
+        }
+    }
+
+    @Test
+    fun testCustomerModule() {
+        val module = SerializersModule {
+            polymorphic(IType::class) {
+                subclass(IntType::class, IntType.serializer())
+                subclass(StringType::class, StringType.serializer())
+            }
+        }
+
+        val buf = ProtoBuf { serializersModule = module }
+
+        val dataInt = OneOfData(IntType(42), "foo")
+        val intString = buf.encodeToHexString(OneOfData.serializer(), dataInt)
+        /**
+         * 5:VARINT 42
+         * 3:LEN {"foo"}
+         */
+        assertEquals("282a1a03666f6f", intString)
+
+        val dataString = OneOfData(StringType("bar"), "foo")
+        val stringString = buf.encodeToHexString(OneOfData.serializer(), dataString)
+        /**
+         * 6:LEN {"bar"}
+         * 3:LEN {"foo"}
+         */
+        assertEquals("32036261721a03666f6f", stringString)
+        val stringData = buf.decodeFromHexString<OneOfData>(stringString)
+        assertEquals(stringData, dataString)
+        val intData = buf.decodeFromHexString<OneOfData>(intString)
+        assertEquals(intData, dataInt)
+    }
+
+    @Serializable
+    data class FailWithClass(@ProtoOneOf val i: IFailType, @ProtoNumber(3) val name: String)
+
+    @Serializable
+    sealed interface IFailType
+
+    @Serializable
+    data class FailIntType(val i: Int) : IFailType
+
+    @Test
+    fun testFailWithClassEncoding() {
+        val data = FailWithClass(FailIntType(42), "foo")
+        assertFailsWithMessage<IllegalArgumentException>(
+            "Implementation of oneOf type kotlinx.serialization.protobuf.ProtobufOneOfTest.FailIntType should have @ProtoNumber annotation"
+        ) { ProtoBuf.encodeToHexString(FailWithClass.serializer(), data) }
+    }
+
+    @Test
+    fun testFailWithClassDecoding() {
+        assertFailsWithMessage<IllegalArgumentException>(
+            "kotlinx.serialization.protobuf.ProtobufOneOfTest.FailIntType implementing oneOf type kotlinx.serialization.protobuf.ProtobufOneOfTest.IFailType should have @ProtoNumber annotation in its single property."
+        ) {
+            ProtoBuf.decodeFromHexString<FailWithClass>(
+                /**
+                 * 5:VARINT 42
+                 * 3:LEN {"foo"}
+                 */
+                "282a1a03666f6f"
+            )
+        }
+    }
+
+    @Serializable
+    data class CustomOuter(@ProtoOneOf val inner: CustomInner)
+
+    @Serializable
+    abstract class CustomInner
+
+    data class CustomInnerInt(val i: Int) : CustomInner()
+
+    object CustomerInnerIntSerializer : KSerializer<CustomInnerInt> {
+        override val descriptor: SerialDescriptor
+            get() = buildClassSerialDescriptor("CustomInnerInt") {
+                element<Int>(
+                    "i",
+                    annotations = listOf(ProtoNumber(1)),
+                )
+            }
+
+        override fun deserialize(decoder: Decoder): CustomInnerInt {
+            return decoder.decodeStructure(descriptor) {
+                var value = 0
+                while (true) {
+                    when (val index = decodeElementIndex(descriptor)) {
+                        0 -> value = decodeIntElement(descriptor, index)
+                        CompositeDecoder.DECODE_DONE -> break
+                    }
+                }
+                CustomInnerInt(value)
+            }
+        }
+
+        override fun serialize(encoder: Encoder, value: CustomInnerInt) = encoder.encodeStructure(descriptor) {
+            encodeIntElement(descriptor, 0, value.i)
+        }
+
+    }
+
+    @Test
+    fun testCustom() {
+        val module = SerializersModule {
+            polymorphic(CustomInner::class) {
+                subclass(CustomInnerInt::class, CustomerInnerIntSerializer)
+            }
+        }
+        val data = CustomOuter(CustomInnerInt(42))
+        val buf = ProtoBuf { serializersModule = module }
+        /**
+         * 1:VARINT 42
+         */
+        assertEquals("082a", buf.encodeToHexString(CustomOuter.serializer(), data))
+    }
+
+    @Serializable
+    data class CustomAnyData(@ProtoOneOf @Polymorphic val inner: Any)
+
+    @Test
+    fun testCustomAny() {
+        val module = SerializersModule {
+            polymorphic(Any::class) {
+                subclass(CustomInnerInt::class, CustomerInnerIntSerializer)
+            }
+        }
+        val data = CustomAnyData(CustomInnerInt(42))
+        val buf = ProtoBuf { serializersModule = module }
+        /**
+         * 1:VARINT 42
+         */
+        assertEquals(data, buf.decodeFromHexString<CustomAnyData>("082a"))
+    }
+
+    @Serializable
+    data class TypedIntOuter(
+        @ProtoOneOf val i: ITypedInt,
+    )
+
+    @Serializable
+    sealed interface ITypedInt
+
+    @Serializable
+    data class Fixed32Int(
+        @ProtoNumber(2)
+        @ProtoType(ProtoIntegerType.FIXED)
+        val value: Int
+    ) : ITypedInt
+
+    @Serializable
+    @JvmInline
+    value class Fixed32Long(
+        @ProtoNumber(3)
+        @ProtoType(ProtoIntegerType.FIXED)
+        val value: Long
+    ) : ITypedInt
+
+    @Serializable
+    @JvmInline
+    value class SignedInt(
+        @ProtoNumber(4)
+        @ProtoType(ProtoIntegerType.SIGNED)
+        val value: Int
+    ) : ITypedInt
+
+    @Serializable
+    data class SignedLong(
+        @ProtoNumber(5)
+        @ProtoType(ProtoIntegerType.SIGNED)
+        val value: Long
+    ) : ITypedInt
+
+    @Serializable
+    data class DefaultInt(
+        @ProtoNumber(6)
+        @ProtoType(ProtoIntegerType.DEFAULT)
+        val value: Int
+    ) : ITypedInt
+
+    @Serializable
+    data class DefaultLong(
+        @ProtoNumber(7)
+        @ProtoType(ProtoIntegerType.DEFAULT)
+        val value: Long
+    ) : ITypedInt
+
+    @Test
+    fun testTypedInt() {
+        val fixed = TypedIntOuter(Fixed32Int(32))
+        ProtoBuf.encodeToHexString(fixed).also {
+            /**
+             * 2:I32 32i32
+             */
+            assertEquals("1520000000", it)
+        }
+        /**
+         * 2:I32 32i32
+         */
+        ProtoBuf.decodeFromHexString<TypedIntOuter>("1520000000").also {
+            assertEquals(fixed, it)
+        }
+        val fixedLong = TypedIntOuter(Fixed32Long(30576774159))
+        ProtoBuf.encodeToHexString(fixedLong).also {
+            /**
+             * 3:VARINT 30576774159
+             */
+            assertEquals("188f9892f471", it)
+        }
+        /**
+         * 3:VARINT 30576774159
+         */
+        ProtoBuf.decodeFromHexString<TypedIntOuter>("188f9892f471").also {
+            assertEquals(fixedLong, it)
+        }
+        val signed = TypedIntOuter(SignedInt(32))
+        ProtoBuf.encodeToHexString(signed).also {
+            /**
+             * 4:VARINT 32
+             */
+            assertEquals("2020", it)
+        }
+        /**
+         * 4:VARINT 32
+         */
+        ProtoBuf.decodeFromHexString<TypedIntOuter>("2020").also {
+            assertEquals(signed, it)
+        }
+        val signedLong = TypedIntOuter(SignedLong(30576774159))
+        ProtoBuf.encodeToHexString(signedLong).also {
+            /**
+             * 5:VARINT 61153548318
+             */
+            assertEquals("289eb0a4e8e301", it)
+        }
+        /**
+         * 5:VARINT 61153548318
+         */
+        ProtoBuf.decodeFromHexString<TypedIntOuter>("289eb0a4e8e301").also {
+            assertEquals(signedLong, it)
+        }
+        val default = TypedIntOuter(DefaultInt(32))
+        ProtoBuf.encodeToHexString(default).also {
+            /**
+             * 6:VARINT 32
+             */
+            assertEquals("3020", it)
+        }
+        /**
+         * 6:VARINT 32
+         */
+        ProtoBuf.decodeFromHexString<TypedIntOuter>("3020").also {
+            assertEquals(default, it)
+        }
+        val defaultLong = TypedIntOuter(DefaultLong(30576774159))
+        ProtoBuf.encodeToHexString(defaultLong).also {
+            /**
+             * 7:VARINT 30576774159
+             */
+            assertEquals("388f9892f471", it)
+        }
+        /**
+         * 7:VARINT 30576774159
+         */
+        ProtoBuf.decodeFromHexString<TypedIntOuter>("388f9892f471").also {
+            assertEquals(defaultLong, it)
+        }
+    }
+
+    // @ProtoNumber(777) here should be ignored
+    @Serializable
+    data class Dummy(@ProtoNumber(777) @ProtoOneOf val i: DummyInterface)
+
+    @Serializable
+    sealed interface DummyInterface
+    @Serializable
+    data class DummyInt(@ProtoNumber(1) val i: Int) : DummyInterface
+
+    @Test
+    fun testDummyAnnotation() {
+        val data = Dummy(DummyInt(42))
+        ProtoBuf.encodeToHexString(data).also {
+            /**
+             * 1:VARINT 42
+             */
+            assertEquals("082a", it)
+        }
+        /**
+         * 1:VARINT 42
+         */
+        ProtoBuf.decodeFromHexString<Dummy>("082a").also {
+            assertEquals(data, it)
+        }
+    }
+
+    @Serializable
+    data class Outer(@ProtoOneOf val inner: Inner)
+
+    @Serializable
+    data class Inner(@ProtoNumber(1) val i: Int) // not sealed or abstract
+
+    @Test
+    fun testNonePolymorphicClass() {
+        val data = Outer(Inner(42))
+        assertFailsWithMessage<IllegalArgumentException>(
+            "The serializer of one of type kotlinx.serialization.protobuf.ProtobufOneOfTest.Inner should be using generic polymorphic serializer, but got CLASS."
+        ) {
+            // Fails in [kotlinx.serialization.protobuf.internal.OneOfPolymorphicEncoder.init]
+            ProtoBuf.encodeToHexString(data)
+        }
+
+        assertFailsWithMessage<IllegalArgumentException>(
+            "Class kotlinx.serialization.protobuf.ProtobufOneOfTest.Inner should be abstract or sealed or interface to be used as @ProtoOneOf property."
+        ) {
+            // Fails in [kotlinx.serialization.protobuf.internal.HelpersKt.getAllOneOfSerializerOfField]
+            ProtoBuf.decodeFromHexString<Outer>("082a")
+        }
+    }
+}
\ No newline at end of file
diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufPrimitivesTest.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufPrimitivesTest.kt
index 56c7bfa..f871644 100644
--- a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufPrimitivesTest.kt
+++ b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufPrimitivesTest.kt
@@ -3,18 +3,10 @@
  */
 package kotlinx.serialization.protobuf
 
-import kotlinx.serialization.*
 import kotlinx.serialization.builtins.*
 import kotlin.test.*
 
 class ProtobufPrimitivesTest {
-
-    private fun <T> testConversion(data: T, serializer: KSerializer<T>, expectedHexString: String) {
-        val string = ProtoBuf.encodeToHexString(serializer, data).uppercase()
-        assertEquals(expectedHexString, string)
-        assertEquals(data, ProtoBuf.decodeFromHexString(serializer, string))
-    }
-
     @Test
     fun testPrimitiveTypes() {
         testConversion(true, Boolean.serializer(), "01")
diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufTypeParameterTest.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufTypeParameterTest.kt
new file mode 100644
index 0000000..0b0f19f
--- /dev/null
+++ b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/ProtobufTypeParameterTest.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.protobuf
+
+import kotlinx.serialization.*
+import kotlin.test.*
+
+class ProtobufTypeParameterTest {
+    @Serializable
+    data class Box<T>(val value: T)
+
+    @Serializable
+    data class ExplicitNullableUpperBoundBox<T : Any?>(val value: T)
+
+    @Serializable
+    data class ExplicitNullableUpperNullablePropertyBoundBox<T : Any?>(val value: T?)
+
+    inline fun <reified T> testBox(value: T, expectedHexString: String) {
+        testConversion(Box(value), expectedHexString)
+        testConversion(ExplicitNullableUpperBoundBox(value), expectedHexString)
+        testConversion(ExplicitNullableUpperNullablePropertyBoundBox(value), expectedHexString)
+    }
+
+    @Serializable
+    private data class DefaultArgPair<T>(val first: T, val second: T = first)
+
+    companion object {
+        val testList0 = emptyList<Int>()
+        val testList1 = listOf(0)
+        val testMap0 = emptyMap<Int, Int>()
+        val testMap1 = mapOf(0 to 0)
+    }
+
+
+    @Test
+    fun testNothingBoxesWithNull() {
+        // Cannot use 'Nothing?' as reified type parameter
+        //testBox(null, "")
+        testConversion(Box(null), "")
+        testConversion(ExplicitNullableUpperBoundBox(null), "")
+        @Suppress("RemoveExplicitTypeArguments")
+        testConversion(ExplicitNullableUpperNullablePropertyBoundBox<Nothing>(null), "")
+        testConversion(ExplicitNullableUpperNullablePropertyBoundBox<Nothing?>(null), "")
+    }
+
+    @Test
+    fun testIntBoxes() {
+        testBox(0, "0800")
+        testBox(1, "0801")
+    }
+
+    @Test
+    fun testNullableIntBoxes() {
+        testBox<Int?>(null, "")
+        testBox<Int?>(0, "0800")
+    }
+
+    @Test
+    fun testCollectionBoxes() {
+        testBox(testList0, "")
+        testBox(testList1, "0800")
+        testBox(testMap0, "")
+        testBox(testMap1, "0A0408001000")
+    }
+
+    @Test
+    fun testNullableCollectionBoxes() {
+        fun assertFailsForNullForCollectionTypes(block: () -> Unit) {
+            try {
+                block()
+                fail()
+            } catch (e: SerializationException) {
+                assertEquals(
+                    "'null' is not supported for collection types in ProtoBuf", e.message
+                )
+            }
+        }
+        assertFailsForNullForCollectionTypes {
+            testBox<List<Int>?>(null, "")
+        }
+        assertFailsForNullForCollectionTypes {
+            testBox<Map<Int, Int>?>(null, "")
+        }
+        testBox<List<Int>?>(testList0, "")
+        testBox<Map<Int, Int>?>(testMap0, "")
+    }
+
+    @Test
+    fun testWithDefaultArguments() {
+        testConversion(DefaultArgPair(null), "")
+        testConversion(DefaultArgPair(1), "0801")
+        testConversion(DefaultArgPair(null, null), "")
+        testConversion(DefaultArgPair(null, 1), "1001")
+        assertFailsWith<SerializationException> {
+            testConversion(DefaultArgPair(1, null), "0801")
+        }
+        testConversion(DefaultArgPair(1, 1), "0801")
+        testConversion(DefaultArgPair(1, 2), "08011002")
+    }
+}
\ No newline at end of file
diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/SkipFieldsTest.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/SkipFieldsTest.kt
new file mode 100644
index 0000000..1d60d95
--- /dev/null
+++ b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/SkipFieldsTest.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.protobuf
+
+
+import kotlinx.serialization.*
+import kotlin.test.*
+
+class SkipFieldsTest {
+
+    @Serializable
+    data class Holder(val value: Int)
+
+    @Test
+    fun testSkipBigFieldNumber() {
+        // first value with id = 2047 and takes 2 bytes
+        val hexString = "f87f20082a"
+        val holder = ProtoBuf.decodeFromHexString<Holder>(hexString)
+        assertEquals(42, holder.value)
+    }
+
+    @Test
+    fun testSkipUnknownFiledNumberForString() {
+        // first value is size delimited (string) with id = 42
+        val hexString = "d2020c48656c6c6f20576f726c6421082a"
+        val holder = ProtoBuf.decodeFromHexString<Holder>(hexString)
+        assertEquals(42, holder.value)
+    }
+}
\ No newline at end of file
diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/TestFunctionTest.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/TestFunctionTest.kt
new file mode 100644
index 0000000..823422c
--- /dev/null
+++ b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/TestFunctionTest.kt
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.protobuf
+
+import kotlinx.serialization.protobuf.internal.ProtobufDecodingException
+import kotlin.test.Ignore
+import kotlin.test.Test
+
+/**
+ * Tests for [assertFailsWith] to see if output in IDEA can be checked with <Click to see difference> button.
+ * Expected to fail so ignore in CI.
+ */
+@Ignore
+class TestFunctionTest {
+    @Test
+    fun testAssertionMessage() {
+        assertFailsWith<IllegalArgumentException>(assertion = {
+            assertFailsWith("expected message")
+        }) {
+            throw IllegalArgumentException("actual message")
+        }
+    }
+    @Test
+    fun testAssertionType() {
+        assertFailsWith<IllegalArgumentException>(assertion = {
+            assertFailsWith("")
+            assertCausedBy<ProtobufDecodingException> {
+                assertFailsWith("expected message")
+            }
+        }) {
+            throw IllegalArgumentException("", IllegalArgumentException())
+        }
+    }
+    @Test
+    fun testAssertionFailWith() {
+        assertFailsWith<NumberFormatException>(assertion = {}) {
+            throw ProtobufDecodingException("expected message")
+        }
+    }
+}
\ No newline at end of file
diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/TestFunctions.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/TestFunctions.kt
new file mode 100644
index 0000000..d886e0f
--- /dev/null
+++ b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/TestFunctions.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package kotlinx.serialization.protobuf
+
+import kotlinx.serialization.*
+import kotlin.reflect.KClass
+import kotlin.test.*
+
+fun <T> testConversion(data: T, serializer: KSerializer<T>, expectedHexString: String) {
+    val string = ProtoBuf.encodeToHexString(serializer, data).uppercase()
+    assertEquals(expectedHexString, string)
+    assertEquals(data, ProtoBuf.decodeFromHexString(serializer, string))
+}
+
+inline fun <reified T> testConversion(data: T, expectedHexString: String) {
+    val string = ProtoBuf.encodeToHexString(data).uppercase()
+    assertEquals(expectedHexString, string)
+    assertEquals(data, ProtoBuf.decodeFromHexString(string))
+}
+
+inline fun <reified T : Throwable> assertFailsWithMessage(
+    message: String,
+    assertionMessage: String? = null,
+    block: () -> Unit
+) {
+    assertFailsWith<T>(
+        assertionMessage,
+        {
+            assertFailsWith(message)
+        },
+        block,
+    )
+}
+
+@DslMarker
+annotation class ExceptionCheckDsl
+
+@ExceptionCheckDsl
+interface ExceptionCheckScope<T> {
+    fun assertFailsWith(vararg message: String)
+    fun <R : Throwable> assertCausedBy(byType: KClass<R>, assertion: ExceptionCheckScope<R>.() -> Unit)
+}
+
+@ExceptionCheckDsl
+inline fun <reified R : Throwable> ExceptionCheckScope<*>.assertCausedBy(noinline assertion: ExceptionCheckScope<R>.() -> Unit) {
+    assertCausedBy(R::class, assertion)
+}
+
+inline fun <reified T : Throwable> assertFailsWith(
+    assertionMessage: String? = null,
+    assertion: ExceptionCheckScope<T>.() -> Unit = {},
+    block: () -> Unit
+) {
+    val exception = assertFailsWith(T::class, assertionMessage, block = block)
+    val scope = buildExceptionCheckScope(exception)
+    scope.assertion()
+}
+
+fun <T : Throwable> buildExceptionCheckScope(exception: T, depth: Int = 0): ExceptionCheckScope<T> =
+    object : ExceptionCheckScope<T> {
+        override fun assertFailsWith(vararg message: String) {
+            val exceptionStackSize = exception.exceptionStackSize
+            assertTrue(
+                message.size <= exceptionStackSize,
+                "Expected exception to be assembled by at least ${message.size} throwable(s), but it has $exceptionStackSize, actual exception is $exception."
+            )
+            var index = 0
+            var currentException: Throwable? = exception
+            while (index < message.size) {
+                val currentMessage = message[index]
+                assertNotNull(
+                    currentException,
+                    "Expected exception to have a cause with message $currentMessage, but it was null."
+                )
+                assertEquals(
+                    currentMessage,
+                    currentException.message,
+                    "Exception messages are different at cause stack ${index + depth}."
+                )
+                val nextException = currentException.cause
+                currentException = nextException
+                index++
+            }
+        }
+
+        @Suppress("UNCHECKED_CAST")
+        override fun <R : Throwable> assertCausedBy(byType: KClass<R>, assertion: ExceptionCheckScope<R>.() -> Unit) {
+            val cause = exception.cause
+            assertNotNull(cause, "Expected exception to have a cause of type $byType, but it was null.")
+            assertEquals(
+                byType,
+                cause::class,
+                "Current exception is caused by unexpected exception at cause stack $depth."
+            )
+            buildExceptionCheckScope<R>(cause as R, depth + 1).assertion()
+        }
+
+    }
+
+private val Throwable.exceptionStackSize: Int
+    get() {
+        var count = 1
+        var current = this
+        while (current.cause != null) {
+            count++
+            current = current.cause!!
+        }
+        return count
+    }
\ No newline at end of file
diff --git a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/schema/SchemaValidationsTest.kt b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/schema/SchemaValidationsTest.kt
index 0330250..ffe9d94 100644
--- a/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/schema/SchemaValidationsTest.kt
+++ b/formats/protobuf/commonTest/src/kotlinx/serialization/protobuf/schema/SchemaValidationsTest.kt
@@ -2,8 +2,8 @@
 
 import kotlinx.serialization.*
 import kotlinx.serialization.protobuf.*
-import kotlin.test.Test
-import kotlin.test.assertFailsWith
+import kotlin.jvm.*
+import kotlin.test.*
 
 class SchemaValidationsTest {
     @Serializable
@@ -138,4 +138,44 @@
         assertFailsWith(IllegalArgumentException::class) { ProtoBufSchemaGenerator.generateSchemaText(listOf(FieldNumberDuplicates.serializer().descriptor)) }
         assertFailsWith(IllegalArgumentException::class) { ProtoBufSchemaGenerator.generateSchemaText(listOf(FieldNumberImplicitlyDuplicates.serializer().descriptor)) }
     }
+
+    @Serializable
+    data class OneOfData(
+        @ProtoNumber(1) val name: String,
+        @ProtoOneOf val i: IType
+    )
+
+    @Serializable
+    sealed interface IType
+
+    @Serializable
+    data class IntType(@ProtoNumber(2) val intValue: Int): IType
+
+    @Serializable
+    @JvmInline
+    value class StringType(@ProtoNumber(3) val strValue: String): IType
+
+    @Serializable
+    data class WrapType(@ProtoNumber(4) val content: InnerType): IType
+
+    @Serializable
+    data class InnerType(val innerContent: String)
+
+    @Test
+    fun testOneOfGenerate() {
+        val descriptors = listOf(OneOfData.serializer().descriptor)
+        ProtoBufSchemaGenerator.generateSchemaText(descriptors).also {
+            println(it)
+            assertContains(it, "oneof i")
+            assertContains(it, "message InnerType")
+            // oneof fields need no required keyword
+            assertFalse(it.contains("required int32"))
+        }
+
+        assertFailsWithMessage<IllegalArgumentException>(
+            message = "Implementation of oneOf type kotlinx.serialization.protobuf.ProtobufOneOfTest.FailType should contain only 1 element, but get 2"
+        ) {
+            ProtoBufSchemaGenerator.generateSchemaText(ProtobufOneOfTest.FailOuter.serializer().descriptor)
+        }
+    }
 }
diff --git a/gradle.properties b/gradle.properties
index 099a38f..460a1ce 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -3,24 +3,14 @@
 #
 
 group=org.jetbrains.kotlinx
-version=1.6.4-SNAPSHOT
-
-kotlin.version=1.9.22
-
+version=1.7.4-SNAPSHOT
+jdk_toolchain_version=11
 # This version takes precedence if 'bootstrap' property passed to project
 kotlin.version.snapshot=2.0.255-SNAPSHOT
 # Also set KONAN_LOCAL_DIST environment variable in bootstrap mode to auto-assign konan.home
 
-junit_version=4.12
-jackson_version=2.10.0.pr1
-dokka_version=1.9.10
 native.deploy=
-validator_version=0.13.2
-knit_version=0.5.0
 # Only for tests
-coroutines_version=1.6.4
-kover_version=0.4.2
-okio_version=3.6.0
 
 kover.enabled=true
 
diff --git a/gradle/artifacts.txt b/gradle/artifacts.txt
new file mode 100644
index 0000000..e5f949f
--- /dev/null
+++ b/gradle/artifacts.txt
@@ -0,0 +1,178 @@
+kotlinx-serialization-bom
+kotlinx-serialization-cbor
+kotlinx-serialization-cbor-androidnativearm32
+kotlinx-serialization-cbor-androidnativearm64
+kotlinx-serialization-cbor-androidnativex64
+kotlinx-serialization-cbor-androidnativex86
+kotlinx-serialization-cbor-iosarm64
+kotlinx-serialization-cbor-iossimulatorarm64
+kotlinx-serialization-cbor-iosx64
+kotlinx-serialization-cbor-js
+kotlinx-serialization-cbor-jvm
+kotlinx-serialization-cbor-linuxarm32hfp
+kotlinx-serialization-cbor-linuxarm64
+kotlinx-serialization-cbor-linuxx64
+kotlinx-serialization-cbor-macosarm64
+kotlinx-serialization-cbor-macosx64
+kotlinx-serialization-cbor-mingwx64
+kotlinx-serialization-cbor-tvosarm64
+kotlinx-serialization-cbor-tvossimulatorarm64
+kotlinx-serialization-cbor-tvosx64
+kotlinx-serialization-cbor-wasm-js
+kotlinx-serialization-cbor-wasm-wasi
+kotlinx-serialization-cbor-watchosarm32
+kotlinx-serialization-cbor-watchosarm64
+kotlinx-serialization-cbor-watchosdevicearm64
+kotlinx-serialization-cbor-watchossimulatorarm64
+kotlinx-serialization-cbor-watchosx64
+kotlinx-serialization-core
+kotlinx-serialization-core-androidnativearm32
+kotlinx-serialization-core-androidnativearm64
+kotlinx-serialization-core-androidnativex64
+kotlinx-serialization-core-androidnativex86
+kotlinx-serialization-core-iosarm64
+kotlinx-serialization-core-iossimulatorarm64
+kotlinx-serialization-core-iosx64
+kotlinx-serialization-core-js
+kotlinx-serialization-core-jvm
+kotlinx-serialization-core-linuxarm32hfp
+kotlinx-serialization-core-linuxarm64
+kotlinx-serialization-core-linuxx64
+kotlinx-serialization-core-macosarm64
+kotlinx-serialization-core-macosx64
+kotlinx-serialization-core-mingwx64
+kotlinx-serialization-core-tvosarm64
+kotlinx-serialization-core-tvossimulatorarm64
+kotlinx-serialization-core-tvosx64
+kotlinx-serialization-core-wasm-js
+kotlinx-serialization-core-wasm-wasi
+kotlinx-serialization-core-watchosarm32
+kotlinx-serialization-core-watchosarm64
+kotlinx-serialization-core-watchosdevicearm64
+kotlinx-serialization-core-watchossimulatorarm64
+kotlinx-serialization-core-watchosx64
+kotlinx-serialization-hocon
+kotlinx-serialization-json
+kotlinx-serialization-json-androidnativearm32
+kotlinx-serialization-json-androidnativearm64
+kotlinx-serialization-json-androidnativex64
+kotlinx-serialization-json-androidnativex86
+kotlinx-serialization-json-io
+kotlinx-serialization-json-io-androidnativearm32
+kotlinx-serialization-json-io-androidnativearm64
+kotlinx-serialization-json-io-androidnativex64
+kotlinx-serialization-json-io-androidnativex86
+kotlinx-serialization-json-io-iosarm64
+kotlinx-serialization-json-io-iossimulatorarm64
+kotlinx-serialization-json-io-iosx64
+kotlinx-serialization-json-io-js
+kotlinx-serialization-json-io-jvm
+kotlinx-serialization-json-io-linuxarm32hfp
+kotlinx-serialization-json-io-linuxarm64
+kotlinx-serialization-json-io-linuxx64
+kotlinx-serialization-json-io-macosarm64
+kotlinx-serialization-json-io-macosx64
+kotlinx-serialization-json-io-mingwx64
+kotlinx-serialization-json-io-tvosarm64
+kotlinx-serialization-json-io-tvossimulatorarm64
+kotlinx-serialization-json-io-tvosx64
+kotlinx-serialization-json-io-wasm-js
+kotlinx-serialization-json-io-wasm-wasi
+kotlinx-serialization-json-io-watchosarm32
+kotlinx-serialization-json-io-watchosarm64
+kotlinx-serialization-json-io-watchosdevicearm64
+kotlinx-serialization-json-io-watchossimulatorarm64
+kotlinx-serialization-json-io-watchosx64
+kotlinx-serialization-json-iosarm64
+kotlinx-serialization-json-iossimulatorarm64
+kotlinx-serialization-json-iosx64
+kotlinx-serialization-json-js
+kotlinx-serialization-json-jvm
+kotlinx-serialization-json-linuxarm32hfp
+kotlinx-serialization-json-linuxarm64
+kotlinx-serialization-json-linuxx64
+kotlinx-serialization-json-macosarm64
+kotlinx-serialization-json-macosx64
+kotlinx-serialization-json-mingwx64
+kotlinx-serialization-json-okio
+kotlinx-serialization-json-okio-iosarm64
+kotlinx-serialization-json-okio-iossimulatorarm64
+kotlinx-serialization-json-okio-iosx64
+kotlinx-serialization-json-okio-js
+kotlinx-serialization-json-okio-jvm
+kotlinx-serialization-json-okio-linuxarm64
+kotlinx-serialization-json-okio-linuxx64
+kotlinx-serialization-json-okio-macosarm64
+kotlinx-serialization-json-okio-macosx64
+kotlinx-serialization-json-okio-mingwx64
+kotlinx-serialization-json-okio-tvosarm64
+kotlinx-serialization-json-okio-tvossimulatorarm64
+kotlinx-serialization-json-okio-tvosx64
+kotlinx-serialization-json-okio-wasm-js
+kotlinx-serialization-json-okio-wasm-wasi
+kotlinx-serialization-json-okio-watchosarm32
+kotlinx-serialization-json-okio-watchosarm64
+kotlinx-serialization-json-okio-watchossimulatorarm64
+kotlinx-serialization-json-okio-watchosx64
+kotlinx-serialization-json-tvosarm64
+kotlinx-serialization-json-tvossimulatorarm64
+kotlinx-serialization-json-tvosx64
+kotlinx-serialization-json-wasm-js
+kotlinx-serialization-json-wasm-wasi
+kotlinx-serialization-json-watchosarm32
+kotlinx-serialization-json-watchosarm64
+kotlinx-serialization-json-watchosdevicearm64
+kotlinx-serialization-json-watchossimulatorarm64
+kotlinx-serialization-json-watchosx64
+kotlinx-serialization-properties
+kotlinx-serialization-properties-androidnativearm32
+kotlinx-serialization-properties-androidnativearm64
+kotlinx-serialization-properties-androidnativex64
+kotlinx-serialization-properties-androidnativex86
+kotlinx-serialization-properties-iosarm64
+kotlinx-serialization-properties-iossimulatorarm64
+kotlinx-serialization-properties-iosx64
+kotlinx-serialization-properties-js
+kotlinx-serialization-properties-jvm
+kotlinx-serialization-properties-linuxarm32hfp
+kotlinx-serialization-properties-linuxarm64
+kotlinx-serialization-properties-linuxx64
+kotlinx-serialization-properties-macosarm64
+kotlinx-serialization-properties-macosx64
+kotlinx-serialization-properties-mingwx64
+kotlinx-serialization-properties-tvosarm64
+kotlinx-serialization-properties-tvossimulatorarm64
+kotlinx-serialization-properties-tvosx64
+kotlinx-serialization-properties-wasm-js
+kotlinx-serialization-properties-wasm-wasi
+kotlinx-serialization-properties-watchosarm32
+kotlinx-serialization-properties-watchosarm64
+kotlinx-serialization-properties-watchosdevicearm64
+kotlinx-serialization-properties-watchossimulatorarm64
+kotlinx-serialization-properties-watchosx64
+kotlinx-serialization-protobuf
+kotlinx-serialization-protobuf-androidnativearm32
+kotlinx-serialization-protobuf-androidnativearm64
+kotlinx-serialization-protobuf-androidnativex64
+kotlinx-serialization-protobuf-androidnativex86
+kotlinx-serialization-protobuf-iosarm64
+kotlinx-serialization-protobuf-iossimulatorarm64
+kotlinx-serialization-protobuf-iosx64
+kotlinx-serialization-protobuf-js
+kotlinx-serialization-protobuf-jvm
+kotlinx-serialization-protobuf-linuxarm32hfp
+kotlinx-serialization-protobuf-linuxarm64
+kotlinx-serialization-protobuf-linuxx64
+kotlinx-serialization-protobuf-macosarm64
+kotlinx-serialization-protobuf-macosx64
+kotlinx-serialization-protobuf-mingwx64
+kotlinx-serialization-protobuf-tvosarm64
+kotlinx-serialization-protobuf-tvossimulatorarm64
+kotlinx-serialization-protobuf-tvosx64
+kotlinx-serialization-protobuf-wasm-js
+kotlinx-serialization-protobuf-wasm-wasi
+kotlinx-serialization-protobuf-watchosarm32
+kotlinx-serialization-protobuf-watchosarm64
+kotlinx-serialization-protobuf-watchosdevicearm64
+kotlinx-serialization-protobuf-watchossimulatorarm64
+kotlinx-serialization-protobuf-watchosx64
diff --git a/gradle/benchmark-parsing.gradle b/gradle/benchmark-parsing.gradle
deleted file mode 100644
index b09ef99..0000000
--- a/gradle/benchmark-parsing.gradle
+++ /dev/null
@@ -1,26 +0,0 @@
-import groovy.json.JsonSlurper
-import org.gradle.api.*
-
-/**
- * Utility for printing benchmark results.
- * Results can be obtained with JMH flags
- * -rf json -rff serialization-benchmark-results.json
- */
-class PrintBenchmarksTask extends DefaultTask {
-    private String fileName = "serialization-benchmark-results.json"
-
-    @TaskAction
-    def printBenchmarkJsonAsTeamcityStats() {
-        File jsonFile = project.file(fileName)
-        if (!jsonFile.exists()) throw new TaskExecutionException(this, new FileNotFoundException("File $fileName not found"))
-        def parsedJson = new JsonSlurper().parseText(jsonFile.text)
-
-        parsedJson.each { v ->
-            def name = (v.benchmark - "kotlinx.benchmarks.")
-            def score = v.primaryMetric.score
-            println("##teamcity[buildStatisticValue key='" + name + "' value='" + score + "']")
-        }
-    }
-}
-
-rootProject.tasks.register("printBenchmarksJsonAsTeamcityStats", PrintBenchmarksTask)
diff --git a/gradle/compiler-version.gradle b/gradle/compiler-version.gradle
deleted file mode 100644
index b954529..0000000
--- a/gradle/compiler-version.gradle
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-gradle.taskGraph.whenReady {
-    println("Using Kotlin compiler version: $compilerVersion")
-    if (build_snapshot_train) {
-        configure(subprojects.findAll { it.name == "core" }) {
-            configurations.matching { it.name == "kotlinCompilerClasspath" }.all {
-                println "Manifest of kotlin-compiler-embeddable.jar for serialization"
-                resolvedConfiguration.getFiles().findAll { it.name.contains("kotlin-compiler-embeddable") }.each {
-                    def manifest = zipTree(it).matching {
-                        include 'META-INF/MANIFEST.MF'
-                    }.getFiles().first()
-
-                    manifest.readLines().each {
-                        println it
-                    }
-                }
-            }
-        }
-    }
-}
diff --git a/gradle/configure-source-sets.gradle b/gradle/configure-source-sets.gradle
deleted file mode 100644
index f744b17..0000000
--- a/gradle/configure-source-sets.gradle
+++ /dev/null
@@ -1,198 +0,0 @@
-/*
- * Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-import static KotlinVersion.*
-
-java {
-    toolchain {
-        languageVersion.set(JavaLanguageVersion.of(11))
-    }
-}
-
-tasks.withType(JavaCompile).configureEach {
-    options.release = 8
-}
-
-// Unfortunately there is no compatible version of okio for Wasm WASI target, so we need to skip to configure WASI for json-okio and json-tests.
-// json-tests uses okio with incorporate with other formatter tests so it is hard and not worth to separate it for two projects for WASI.
-// So we disable WASI target in it and we hope, that WASI version of compiler and serialization plugin are identical to the WasmJS target so WASI target is being covered.
-Boolean isOkIoOrFormatTests = (project.name == 'kotlinx-serialization-json-okio' || project.name == 'kotlinx-serialization-json-tests')
-
-kotlin {
-    jvm {
-        withJava()
-        compilations.configureEach {
-            kotlinOptions {
-                jvmTarget = '1.8'
-                freeCompilerArgs += '-Xjdk-release=1.8'
-            }
-        }
-    }
-
-    js {
-        nodejs {
-            testTask {
-                useMocha {
-		    timeout = "10s"
-                }
-            }
-        }
-        configure([compilations.main, compilations.test]) {
-            kotlinOptions {
-                sourceMap = true
-                moduleKind = "umd"
-            }
-        }
-    }
-
-    wasmJs {
-        nodejs()
-    }
-
-    if (!isOkIoOrFormatTests) {
-        wasmWasi {
-            nodejs()
-        }
-    }
-
-    sourceSets.all {
-        kotlin.srcDirs = ["$it.name/src"]
-        resources.srcDirs = ["$it.name/resources"]
-        languageSettings {
-            progressiveMode = true
-
-            optIn("kotlin.ExperimentalMultiplatform")
-            optIn("kotlin.ExperimentalStdlibApi")
-            optIn("kotlinx.serialization.InternalSerializationApi")
-        }
-    }
-
-    sourceSets {
-        commonMain {
-            dependencies {
-                api 'org.jetbrains.kotlin:kotlin-stdlib-common'
-            }
-        }
-
-        commonTest {
-            dependencies {
-                api 'org.jetbrains.kotlin:kotlin-test-common'
-                api 'org.jetbrains.kotlin:kotlin-test-annotations-common'
-            }
-        }
-
-        jvmMain {
-            dependencies {
-                api 'org.jetbrains.kotlin:kotlin-stdlib'
-            }
-        }
-
-        jvmTest {
-            dependencies {
-                api 'org.jetbrains.kotlin:kotlin-test-junit'
-            }
-        }
-
-        jsMain {
-            dependencies {
-                api 'org.jetbrains.kotlin:kotlin-stdlib-js'
-            }
-        }
-
-        jsTest {
-            dependencies {
-                api 'org.jetbrains.kotlin:kotlin-test-js'
-            }
-        }
-
-        create("wasmMain") {
-            dependsOn(commonMain)
-        }
-        create("wasmTest") {
-            dependsOn(commonTest)
-        }
-
-        wasmJsMain {
-            dependsOn(wasmMain)
-            dependencies {
-                api 'org.jetbrains.kotlin:kotlin-stdlib-wasm-js'
-            }
-        }
-
-        wasmJsTest {
-            dependsOn(wasmTest)
-            dependencies {
-                api 'org.jetbrains.kotlin:kotlin-test-wasm-js'
-            }
-        }
-
-        if (!isOkIoOrFormatTests) {
-            wasmWasiMain {
-                dependsOn(wasmMain)
-                dependencies {
-                    api 'org.jetbrains.kotlin:kotlin-stdlib-wasm-wasi'
-                }
-            }
-
-            wasmWasiTest {
-                dependsOn(wasmTest)
-                dependencies {
-                    api 'org.jetbrains.kotlin:kotlin-test-wasm-wasi'
-                }
-            }
-        }
-
-        nativeMain.dependencies {
-        }
-    }
-
-    sourceSets.findAll({ it.name.contains("Test") }).forEach { srcSet ->
-        srcSet.languageSettings {
-            it.optIn("kotlinx.serialization.InternalSerializationApi")
-            it.optIn("kotlinx.serialization.ExperimentalSerializationApi")
-        }
-    }
-
-    sourceSets.matching({ it.name.contains("Main") }).all { srcSet ->
-        project.ext.set("kotlin.mpp.freeCompilerArgsForSourceSet.${srcSet.name}", ["-Xexplicit-api=strict"])
-    }
-
-    targets.all {
-        compilations.all {
-            kotlinOptions {
-                if (rootProject.ext.kotlin_lv_override != null) {
-                    languageVersion = rootProject.ext.kotlin_lv_override
-                    freeCompilerArgs += "-Xsuppress-version-warnings"
-                }
-                freeCompilerArgs += "-Xexpect-actual-classes"
-            }
-        }
-        compilations.main {
-            kotlinOptions {
-                allWarningsAsErrors = true
-            }
-        }
-    }
-
-    def targetsWithoutTestRunners = ["linuxArm64", "linuxArm32Hfp"]
-    configure(targets) {
-        // Configure additional binaries to run tests in the background
-        if (["macos", "linux", "mingw"].any { name.startsWith(it) && !targetsWithoutTestRunners.contains(name) }) {
-            binaries {
-                test("background", [nativeDebugBuild]) {
-                    freeCompilerArgs += ["-trw"]
-                }
-            }
-            testRuns {
-                background { setExecutionSourceFrom(binaries.backgroundDebugTest) }
-            }
-        }
-    }
-}
-
-rootProject.extensions.findByType(org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension.class).with {
-    // canary nodejs that supports recent Wasm GC changes
-    it.nodeVersion = "21.0.0-v8-canary202309167e82ab1fa2"
-    it.nodeDownloadBaseUrl = "https://nodejs.org/download/v8-canary"
-}
\ No newline at end of file
diff --git a/gradle/dokka.gradle b/gradle/dokka.gradle
deleted file mode 100644
index 5a208f2..0000000
--- a/gradle/dokka.gradle
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-apply plugin: 'kotlin'
-apply plugin: 'org.jetbrains.dokka'
-
-def documentedSubprojects = ["kotlinx-serialization-core",
-                             "kotlinx-serialization-json",
-                             "kotlinx-serialization-json-okio",
-                             "kotlinx-serialization-cbor",
-                             "kotlinx-serialization-properties",
-                             "kotlinx-serialization-hocon",
-                             "kotlinx-serialization-protobuf"]
-
-subprojects {
-    if (!(name in documentedSubprojects)) return
-    apply plugin: 'org.jetbrains.dokka'
-    dependencies {
-        dokkaPlugin("org.jetbrains.kotlinx:dokka-pathsaver-plugin:$knit_version")
-    }
-
-    tasks.named('dokkaHtmlPartial') {
-        outputDirectory = file("build/dokka")
-        pluginsMapConfiguration.set(["org.jetbrains.dokka.base.DokkaBase": """{ "templatesDir": "${rootProject.projectDir.toString().replace('\\', '/')}/dokka-templates" }"""])
-
-        dokkaSourceSets {
-            configureEach {
-                includes.from(rootProject.file('dokka/moduledoc.md').path)
-
-                perPackageOption {
-                    matchingRegex.set("kotlinx\\.serialization(\$|\\.).*")
-                    reportUndocumented.set(true)
-                    skipDeprecated.set(true)
-                }
-
-                // Internal API
-                perPackageOption {
-                    matchingRegex.set("kotlinx\\.serialization.internal(\$|\\.).*")
-                    suppress.set(true)
-                }
-
-                // Internal JSON API
-                perPackageOption {
-                    matchingRegex.set("kotlinx\\.serialization.json.internal(\$|\\.).*")
-                    suppress.set(true)
-                    reportUndocumented.set(false)
-                }
-
-                // Workaround for typealias
-                perPackageOption {
-                    matchingRegex.set("kotlinx\\.serialization.protobuf.internal(\$|\\.).*")
-                    suppress.set(true)
-                    reportUndocumented.set(false)
-                }
-
-                // Deprecated migrations
-                perPackageOption {
-                    matchingRegex.set("kotlinx\\.protobuf(\$|\\.).*")
-                    reportUndocumented.set(true)
-                    skipDeprecated.set(true)
-                }
-
-                // Deprecated migrations
-                perPackageOption {
-                    matchingRegex.set("org\\.jetbrains\\.kotlinx\\.serialization\\.config(\$|\\.).*")
-                    reportUndocumented.set(false)
-                    skipDeprecated.set(true)
-                }
-
-                // JS/Native implementation of JVM-only `org.intellij.lang.annotations.Language` class to add syntax support by IDE.
-                perPackageOption {
-                    matchingRegex.set("org\\.intellij\\.lang\\.annotations(\$|\\.).*")
-                    suppress.set(true)
-                }
-
-                sourceLink {
-                    localDirectory.set(rootDir)
-                    remoteUrl.set(new URL("https://github.com/Kotlin/kotlinx.serialization/tree/master"))
-                    remoteLineSuffix.set("#L")
-                }
-            }
-        }
-    }
-}
-
-// Knit relies on Dokka task and it's pretty convenient
-task dokka(dependsOn: dokkaHtmlMultiModule) {}
diff --git a/gradle/kover.gradle b/gradle/kover.gradle
deleted file mode 100644
index 31b03eb..0000000
--- a/gradle/kover.gradle
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-apply plugin: 'kover'
-
-tasks.withType(Test) { task ->
-    kover {
-        enabled = rootProject.ext.koverEnabled
-
-    }
-}
-tasks.koverVerify {
-    // Core is mainly uncovered because a lot of serializers are tested with JSON
-    def minPercentage = (project.name.contains("core") || project.name.contains("properties")|| project.name.contains("json-okio")) ? 44 : 80
-    rule {
-        name = "Minimal line coverage rate in percents"
-        bound {
-            minValue = minPercentage
-            // valueType is 'COVERED_LINES_PERCENTAGE' by default
-        }
-    }
-}
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
new file mode 100644
index 0000000..e4ad3d9
--- /dev/null
+++ b/gradle/libs.versions.toml
@@ -0,0 +1,63 @@
+[versions]
+kotlin = "2.0.20"
+kover = "0.8.2"
+dokka = "1.9.20"
+knit = "0.5.0"
+bcv = "0.16.2"
+animalsniffer = "1.7.1"
+protobuf = "0.8.19"
+shadow = "8.1.1"
+jmh = "0.7.2"
+jmh-core = "1.37"
+guava = "31.1-jre"
+guava24 = "24.1.1-jre"
+jackson = "2.13.3"
+okio = "3.9.0"
+kotlinx-io="0.4.0"
+gson = "2.8.5"
+moshi = "1.15.1"
+kotlintest = "2.0.7"
+coroutines = "1.6.4"
+cbor = "4.2.0"
+typesafe-config = "1.4.1"
+junit4 = "4.12"
+protoc = "3.17.3"
+
+[libraries]
+gradlePlugin-kotlin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin"}
+gradlePlugin-kover = { module = "org.jetbrains.kotlinx:kover-gradle-plugin", version.ref = "kover"}
+gradlePlugin-dokka = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version.ref = "dokka"}
+gradlePlugin-animalsniffer = { module = "ru.vyarus:gradle-animalsniffer-plugin", version.ref = "animalsniffer"}
+gradlePlugin-binaryCompatibilityValidator = { module = "org.jetbrains.kotlinx:binary-compatibility-validator", version.ref = "bcv"}
+
+kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
+kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
+
+dokka-pathsaver = { module = "org.jetbrains.kotlinx:dokka-pathsaver-plugin", version.ref = "knit"}
+knitTest = { module = "org.jetbrains.kotlinx:kotlinx-knit-test", version.ref = "knit"}
+jmhCore = { module = "org.openjdk.jmh:jmh-core", version.ref = "jmh-core"}
+guava = { module = "com.google.guava:guava", version.ref = "guava"}
+guava-24 = { module = "com.google.guava:guava", version.ref = "guava24"}
+jackson-core = { module = "com.fasterxml.jackson.core:jackson-core", version.ref = "jackson"}
+jackson-databind = { module = "com.fasterxml.jackson.core:jackson-databind", version.ref = "jackson"}
+jackson-module-kotlin = { module = "com.fasterxml.jackson.module:jackson-module-kotlin", version.ref = "jackson"}
+jackson-cbor = { module = "com.fasterxml.jackson.dataformat:jackson-dataformat-cbor", version.ref = "jackson"}
+okio = { module = "com.squareup.okio:okio", version.ref = "okio"}
+kotlinx-io = { module = "org.jetbrains.kotlinx:kotlinx-io-core", version.ref = "kotlinx-io"}
+gson = { module = "com.google.code.gson:gson", version.ref = "gson"}
+kotlintest = { module = "io.kotlintest:kotlintest", version.ref = "kotlintest"}
+coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines"}
+cbor = { module = "com.upokecenter:cbor", version.ref = "cbor"}
+typesafe-config = { module = "com.typesafe:config", version.ref = "typesafe-config"}
+junit-junit4 = { module = "junit:junit", version.ref = "junit4"}
+protoc = { module = "com.google.protobuf:protoc", version.ref = "protoc"}
+protobuf-java = { module = "com.google.protobuf:protobuf-java", version.ref = "protoc" }
+moshi-kotlin = { module = "com.squareup.moshi:moshi-kotlin", version.ref = "moshi" }
+moshi-codegen = { module = "com.squareup.moshi:moshi-kotlin-codegen", version.ref = "moshi" }
+
+[plugins]
+knit = { id = "org.jetbrains.kotlinx.knit", version.ref = "knit" }
+jmh = { id = "me.champeau.jmh", version.ref = "jmh" }
+shadow = { id = "com.github.johnrengelman.shadow", version.ref = "shadow" }
+protobuf = { id = "com.google.protobuf", version.ref = "protobuf" }
+serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
diff --git a/gradle/maven-metadata.gradle b/gradle/maven-metadata.gradle
deleted file mode 100644
index 5e4148c..0000000
--- a/gradle/maven-metadata.gradle
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-project.ext.pomConfig = {
-    licenses {
-        license {
-            name "The Apache Software License, Version 2.0"
-            url "http://www.apache.org/licenses/LICENSE-2.0.txt"
-            distribution "repo"
-        }
-    }
-    developers {
-        developer {
-            id "JetBrains"
-            name "JetBrains Team"
-            organization "JetBrains"
-            organizationUrl "http://www.jetbrains.com"
-        }
-    }
-
-    scm {
-        url "https://github.com/Kotlin/kotlinx.serialization"
-    }
-}
-
-project.ext.configureMavenCentralMetadata = { pom ->
-    def root = asNode()
-    root.appendNode('name', project.name)
-    root.appendNode('description', 'Kotlin multiplatform serialization runtime library')
-    root.appendNode('url', 'https://github.com/Kotlin/kotlinx.serialization')
-    root.children().last() + pomConfig
-}
diff --git a/gradle/native-targets.gradle b/gradle/native-targets.gradle
deleted file mode 100644
index 8ef7f48..0000000
--- a/gradle/native-targets.gradle
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-static def doesNotDependOnOkio(project) {
-    return !project.name.contains("json-okio") && !project.name.contains("json-tests")
-}
-
-kotlin {
-    applyDefaultHierarchyTemplate {
-
-        // According to https://kotlinlang.org/docs/native-target-support.html
-        // Tier 1
-        macosX64()
-        macosArm64()
-        iosSimulatorArm64()
-        iosX64()
-
-        // Tier 2
-        linuxX64()
-        linuxArm64()
-        watchosSimulatorArm64()
-        watchosX64()
-        watchosArm32()
-        watchosArm64()
-        tvosSimulatorArm64()
-        tvosX64()
-        tvosArm64()
-        iosArm64()
-
-        // Tier 3
-        mingwX64()
-        // https://github.com/square/okio/issues/1242#issuecomment-1759357336
-        if (doesNotDependOnOkio(project)) {
-            androidNativeArm32()
-            androidNativeArm64()
-            androidNativeX86()
-            androidNativeX64()
-            watchosDeviceArm64()
-
-            // Deprecated, but not removed
-            linuxArm32Hfp()
-        }
-    }
-}
diff --git a/gradle/publish-mpp-root-module-in-platform.gradle b/gradle/publish-mpp-root-module-in-platform.gradle
deleted file mode 100644
index 7362ffb..0000000
--- a/gradle/publish-mpp-root-module-in-platform.gradle
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * Copyright 2014-2020 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
- */
-
-
-/**
- * Publish the platform JAR and POM so that consumers who depend on this module and can't read Gradle module
- * metadata can still get the platform artifact and transitive dependencies from the POM
- * (see details in https://youtrack.jetbrains.com/issue/KT-39184#focus=streamItem-27-4115233.0-0)
- */
-project.ext.publishPlatformArtifactsInRootModule = { MavenPublication platformPublication ->
-    afterEvaluate {
-        XmlProvider platformXml = null
-
-        platformPublication.pom.withXml { platformXml = it }
-
-        publishing.publications.kotlinMultiplatform {
-            pom.withXml {
-                Node root = asNode()
-                // Remove the original content and add the content from the platform POM:
-                root.children().toList().each { root.remove(it as Node) }
-                platformXml.asNode().children().each { root.append(it as Node) }
-
-                // Adjust the self artifact ID, as it should match the root module's coordinates:
-                ((root.get("artifactId") as NodeList).get(0) as Node).setValue(artifactId)
-
-                // Set packaging to POM to indicate that there's no artifact:
-                root.appendNode("packaging", "pom")
-
-                // Remove the original platform dependencies and add a single dependency on the platform module:
-                Node dependencies = (root.get("dependencies") as NodeList).get(0) as Node
-                dependencies.children().toList().each { dependencies.remove(it as Node) }
-                Node singleDependency = dependencies.appendNode("dependency")
-                singleDependency.appendNode("groupId", platformPublication.groupId)
-                singleDependency.appendNode("artifactId", platformPublication.artifactId)
-                singleDependency.appendNode("version", platformPublication.version)
-                singleDependency.appendNode("scope", "compile")
-            }
-        }
-
-        tasks.matching { it.name == "generatePomFileForKotlinMultiplatformPublication"}.configureEach {
-            dependsOn(tasks["generatePomFileFor${platformPublication.name.capitalize()}Publication"])
-        }
-    }
-}
\ No newline at end of file
diff --git a/gradle/publishing.gradle b/gradle/publishing.gradle
deleted file mode 100644
index 2c3518e..0000000
--- a/gradle/publishing.gradle
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-// Configures publishing of Maven artifacts to MavenCentral
-
-apply plugin: 'maven-publish'
-apply plugin: 'signing'
-
-apply from: project.rootProject.file('gradle/maven-metadata.gradle')
-
-def isMultiplatform = project.name in ["kotlinx-serialization-core", "kotlinx-serialization-json", "kotlinx-serialization-json-okio",
-                                       "kotlinx-serialization-json-tests", "kotlinx-serialization-protobuf", "kotlinx-serialization-cbor",
-                                       "kotlinx-serialization-properties"]
-def isBom = project.name == "kotlinx-serialization-bom"
-
-if (!isBom) {
-    task stubSources(type: Jar) {
-        archiveClassifier = 'sources'
-    }
-
-    task stubJavadoc(type: Jar) {
-        archiveClassifier = 'javadoc'
-    }
-}
-
-task emptyJar(type: Jar) {
-}
-
-afterEvaluate {
-    task mainSourcesJar(type: Jar) {
-        classifier = 'sources'
-        if (isMultiplatform) {
-            from kotlin.sourceSets.commonMain.kotlin
-        } else if (isBom) {
-            // no-op: sourceSets is [] for BOM, as it does not have sources.
-        } else {
-            from sourceSets.main.allSource
-        }
-    }
-}
-
-afterEvaluate {
-    publishing {
-        def variantName = "${project.name}"
-
-        if (!isMultiplatform && !isBom) {
-            publications {
-                maven(MavenPublication) { publication ->
-                    artifactId variantName
-                    publication.from components.java
-                    publication.artifact mainSourcesJar
-                    artifact stubJavadoc
-
-                    PublishingKt.configureMavenCentralMetadata(publication.pom, project)
-                    PublishingKt.signPublicationIfKeyPresent(project, publication)
-                }
-            }
-
-            return
-        }
-
-        // Rename artifacts for backward compatibility
-        publications.all {
-            def type = it.name
-            logger.info("Configuring $type")
-            switch (type) {
-                case 'kotlinMultiplatform':
-                        // With Kotlin 1.4.0, the root module ID has no suffix, but for compatibility with
-                        // the consumers who can't read Gradle module metadata, we publish the JVM artifacts in it
-                    it.artifactId = variantName
-                    apply from: "$rootDir/gradle/publish-mpp-root-module-in-platform.gradle"
-                    publishPlatformArtifactsInRootModule(publications["jvm"])
-                    break
-                case 'metadata':
-                case 'jvm':
-                case 'js':
-                    it.artifactId = "$variantName-$type"
-                    break
-            }
-            logger.info("Artifact id = ${it.artifactId}")
-
-            PublishingKt.configureMavenCentralMetadata(pom, project)
-            PublishingKt.signPublicationIfKeyPresent(project, it)
-
-            // The 'root' module publishes the JVM module's Javadoc JAR as per publishPlatformArtifactsInRootModule, and
-            // every other module should publish an empty Javadoc JAR. TODO: provide proper documentation artifacts?
-            if (name != "kotlinMultiplatform" && !isBom) {
-                artifact stubJavadoc
-            }
-        }
-    }
-}
-
-publishing {
-    repositories {
-        PublishingKt.configureMavenPublication(delegate, project)
-    }
-}
-
-// Compatibility with old TeamCity configurations that perform :kotlinx-coroutines-core:bintrayUpload
-task bintrayUpload(dependsOn: publish)
-
-// This is required for K/N publishing
-bintrayUpload.dependsOn publishToMavenLocal
-
diff --git a/gradle/teamcity.gradle b/gradle/teamcity.gradle
deleted file mode 100644
index 950494d..0000000
--- a/gradle/teamcity.gradle
+++ /dev/null
@@ -1,14 +0,0 @@
-/*
- * Copyright 2017-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-def teamcitySuffix = project.findProperty("teamcitySuffix")?.toString()
-if (!teamcityInteractionDisabled && project.hasProperty("teamcity") && !(build_snapshot_train || rootProject.properties['build_snapshot_up'])) {
-    // Tell teamcity about version number
-    def postfix = (teamcitySuffix == null) ? "" : " ($teamcitySuffix)"
-    println("##teamcity[buildNumber '${project.version}${postfix}']")
-
-    gradle.taskGraph.beforeTask {
-        println("##teamcity[progressMessage 'Gradle: ${it.project.path}:${it.name}']")
-    }
-}
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
index 7454180..e644113 100644
--- a/gradle/wrapper/gradle-wrapper.jar
+++ b/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 31cca49..b82aa23 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,7 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
index 1b6c787..1aa94a4 100755
--- a/gradlew
+++ b/gradlew
@@ -55,7 +55,7 @@
 #       Darwin, MinGW, and NonStop.
 #
 #   (3) This script is generated from the Groovy template
-#       https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+#       https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
 #       within the Gradle project.
 #
 #       You can find Gradle at https://github.com/gradle/gradle/.
@@ -80,13 +80,11 @@
     esac
 done
 
-APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
-
-APP_NAME="Gradle"
+# This is normally unused
+# shellcheck disable=SC2034
 APP_BASE_NAME=${0##*/}
-
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit
 
 # Use the maximum available, or set MAX_FD != -1 to use that value.
 MAX_FD=maximum
@@ -133,22 +131,29 @@
     fi
 else
     JAVACMD=java
-    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+    if ! command -v java >/dev/null 2>&1
+    then
+        die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
 
 Please set the JAVA_HOME variable in your environment to match the
 location of your Java installation."
+    fi
 fi
 
 # Increase the maximum file descriptors if we can.
 if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
     case $MAX_FD in #(
       max*)
+        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+        # shellcheck disable=SC2039,SC3045
         MAX_FD=$( ulimit -H -n ) ||
             warn "Could not query maximum file descriptor limit"
     esac
     case $MAX_FD in  #(
       '' | soft) :;; #(
       *)
+        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+        # shellcheck disable=SC2039,SC3045
         ulimit -n "$MAX_FD" ||
             warn "Could not set maximum file descriptor limit to $MAX_FD"
     esac
@@ -193,11 +198,15 @@
     done
 fi
 
-# Collect all arguments for the java command;
-#   * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
-#     shell script including quotes and variable substitutions, so put them in
-#     double quotes to make sure that they get re-expanded; and
-#   * put everything else in single quotes, so that it's not re-expanded.
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Collect all arguments for the java command:
+#   * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+#     and any embedded shellness will be escaped.
+#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+#     treated as '${Hostname}' itself on the command line.
 
 set -- \
         "-Dorg.gradle.appname=$APP_BASE_NAME" \
@@ -205,6 +214,12 @@
         org.gradle.wrapper.GradleWrapperMain \
         "$@"
 
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+    die "xargs is not available"
+fi
+
 # Use "xargs" to parse quoted args.
 #
 # With -n1 it outputs one arg per line, with the quotes and backslashes removed.
diff --git a/gradlew.bat b/gradlew.bat
index ac1b06f..7101f8e 100644
--- a/gradlew.bat
+++ b/gradlew.bat
@@ -14,7 +14,7 @@
 @rem limitations under the License.

 @rem

 

-@if "%DEBUG%" == "" @echo off

+@if "%DEBUG%"=="" @echo off

 @rem ##########################################################################

 @rem

 @rem  Gradle startup script for Windows

@@ -25,7 +25,8 @@
 if "%OS%"=="Windows_NT" setlocal

 

 set DIRNAME=%~dp0

-if "%DIRNAME%" == "" set DIRNAME=.

+if "%DIRNAME%"=="" set DIRNAME=.

+@rem This is normally unused

 set APP_BASE_NAME=%~n0

 set APP_HOME=%DIRNAME%

 

@@ -40,13 +41,13 @@
 

 set JAVA_EXE=java.exe

 %JAVA_EXE% -version >NUL 2>&1

-if "%ERRORLEVEL%" == "0" goto execute

+if %ERRORLEVEL% equ 0 goto execute

 

-echo.

-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.

-echo.

-echo Please set the JAVA_HOME variable in your environment to match the

-echo location of your Java installation.

+echo. 1>&2

+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2

+echo. 1>&2

+echo Please set the JAVA_HOME variable in your environment to match the 1>&2

+echo location of your Java installation. 1>&2

 

 goto fail

 

@@ -56,11 +57,11 @@
 

 if exist "%JAVA_EXE%" goto execute

 

-echo.

-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%

-echo.

-echo Please set the JAVA_HOME variable in your environment to match the

-echo location of your Java installation.

+echo. 1>&2

+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2

+echo. 1>&2

+echo Please set the JAVA_HOME variable in your environment to match the 1>&2

+echo location of your Java installation. 1>&2

 

 goto fail

 

@@ -75,13 +76,15 @@
 

 :end

 @rem End local scope for the variables with windows NT shell

-if "%ERRORLEVEL%"=="0" goto mainEnd

+if %ERRORLEVEL% equ 0 goto mainEnd

 

 :fail

 rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of

 rem the _cmd.exe /c_ return code!

-if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1

-exit /b 1

+set EXIT_CODE=%ERRORLEVEL%

+if %EXIT_CODE% equ 0 set EXIT_CODE=1

+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%

+exit /b %EXIT_CODE%

 

 :mainEnd

 if "%OS%"=="Windows_NT" endlocal

diff --git a/guide/build.gradle b/guide/build.gradle
deleted file mode 100644
index b4261e8..0000000
--- a/guide/build.gradle
+++ /dev/null
@@ -1,35 +0,0 @@
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
-
-/*
- * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-apply plugin: 'kotlin'
-apply plugin: 'kotlinx-serialization'
-
-kotlin {
-    jvmToolchain(8)
-}
-
-tasks.withType(KotlinCompile).configureEach {
-    kotlinOptions {
-        if (rootProject.ext.kotlin_lv_override != null) {
-            languageVersion = rootProject.ext.kotlin_lv_override
-            freeCompilerArgs += "-Xsuppress-version-warnings"
-        }
-    }
-}
-
-dependencies {
-    testImplementation "org.jetbrains.kotlin:kotlin-test-junit"
-    testImplementation "org.jetbrains.kotlinx:kotlinx-knit-test:$knit_version"
-    testImplementation project(":kotlinx-serialization-core")
-    testImplementation project(":kotlinx-serialization-json")
-    testImplementation project(":kotlinx-serialization-cbor")
-    testImplementation project(":kotlinx-serialization-protobuf")
-    testImplementation project(":kotlinx-serialization-properties")
-}
-
-sourceSets.test {
-    java.srcDirs("example", "test")
-}
diff --git a/guide/build.gradle.kts b/guide/build.gradle.kts
new file mode 100644
index 0000000..5bafa9b
--- /dev/null
+++ b/guide/build.gradle.kts
@@ -0,0 +1,37 @@
+import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
+import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+
+/*
+ * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+plugins {
+    kotlin("jvm")
+    alias(libs.plugins.serialization)
+}
+
+kotlin {
+    jvmToolchain(8)
+
+    compilerOptions {
+        allWarningsAsErrors = true
+        if (overriddenLanguageVersion != null) {
+            languageVersion = KotlinVersion.fromVersion(overriddenLanguageVersion!!)
+            freeCompilerArgs.add("-Xsuppress-version-warnings")
+        }
+    }
+}
+
+dependencies {
+    testImplementation(libs.knitTest)
+    testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
+    testImplementation(project(":kotlinx-serialization-core"))
+    testImplementation(project(":kotlinx-serialization-json"))
+    testImplementation(project(":kotlinx-serialization-cbor"))
+    testImplementation(project(":kotlinx-serialization-protobuf"))
+    testImplementation(project(":kotlinx-serialization-properties"))
+}
+
+sourceSets.test {
+    java.srcDirs("example", "test")
+}
diff --git a/guide/example/example-classes-10.kt b/guide/example/example-classes-10.kt
index c5a1f73..941bde4 100644
--- a/guide/example/example-classes-10.kt
+++ b/guide/example/example-classes-10.kt
@@ -5,6 +5,7 @@
 import kotlinx.serialization.json.*
 
 @Serializable
+@OptIn(ExperimentalSerializationApi::class) // EncodeDefault is an experimental annotation for now
 data class Project(
     val name: String,
     @EncodeDefault val language: String = "Kotlin"
@@ -12,6 +13,7 @@
 
 
 @Serializable
+@OptIn(ExperimentalSerializationApi::class) // EncodeDefault is an experimental annotation for now
 data class User(
     val name: String,
     @EncodeDefault(EncodeDefault.Mode.NEVER) val projects: List<Project> = emptyList()
diff --git a/guide/example/example-formats-01.kt b/guide/example/example-formats-01.kt
index ad2b3e6..446bc3a 100644
--- a/guide/example/example-formats-01.kt
+++ b/guide/example/example-formats-01.kt
@@ -12,6 +12,7 @@
 @Serializable
 data class Project(val name: String, val language: String)
 
+@OptIn(ExperimentalSerializationApi::class)
 fun main() {
     val data = Project("kotlinx.serialization", "Kotlin") 
     val bytes = Cbor.encodeToByteArray(data)   
diff --git a/guide/example/example-formats-02.kt b/guide/example/example-formats-02.kt
index c95e532..cddccf6 100644
--- a/guide/example/example-formats-02.kt
+++ b/guide/example/example-formats-02.kt
@@ -4,13 +4,14 @@
 import kotlinx.serialization.*
 import kotlinx.serialization.cbor.*
 
-val format = Cbor { ignoreUnknownKeys = true }
-
 @Serializable
 data class Project(val name: String)
 
+@OptIn(ExperimentalSerializationApi::class)
 fun main() {
-    val data = format.decodeFromHexString<Project>(
+  val format = Cbor { ignoreUnknownKeys = true }
+  
+  val data = format.decodeFromHexString<Project>(
         "bf646e616d65756b6f746c696e782e73657269616c697a6174696f6e686c616e6775616765664b6f746c696eff"
     )
     println(data)
diff --git a/guide/example/example-formats-03.kt b/guide/example/example-formats-03.kt
index d2191f6..b48811d 100644
--- a/guide/example/example-formats-03.kt
+++ b/guide/example/example-formats-03.kt
@@ -10,12 +10,14 @@
 }
 
 @Serializable
+@OptIn(ExperimentalSerializationApi::class)
 data class Data(
     @ByteString
     val type2: ByteArray, // CBOR Major type 2
     val type4: ByteArray  // CBOR Major type 4
-)        
+)
 
+@OptIn(ExperimentalSerializationApi::class)
 fun main() {
     val data = Data(byteArrayOf(1, 2, 3, 4), byteArrayOf(5, 6, 7, 8)) 
     val bytes = Cbor.encodeToByteArray(data)   
diff --git a/guide/example/example-formats-04.kt b/guide/example/example-formats-04.kt
index 9f6610b..0ff0289 100644
--- a/guide/example/example-formats-04.kt
+++ b/guide/example/example-formats-04.kt
@@ -12,6 +12,7 @@
 @Serializable
 data class Project(val name: String, val language: String)
 
+@OptIn(ExperimentalSerializationApi::class)
 fun main() {
     val data = Project("kotlinx.serialization", "Kotlin") 
     val bytes = ProtoBuf.encodeToByteArray(data)   
diff --git a/guide/example/example-formats-05.kt b/guide/example/example-formats-05.kt
index 796e3eb..b65e1cd 100644
--- a/guide/example/example-formats-05.kt
+++ b/guide/example/example-formats-05.kt
@@ -9,6 +9,7 @@
         "{${it.toUByte().toString(16).padStart(2, '0').uppercase()}}"
 }
 
+@OptIn(ExperimentalSerializationApi::class)
 @Serializable
 data class Project(
     @ProtoNumber(1)
@@ -17,6 +18,7 @@
     val language: String
 )
 
+@OptIn(ExperimentalSerializationApi::class)
 fun main() {
     val data = Project("kotlinx.serialization", "Kotlin") 
     val bytes = ProtoBuf.encodeToByteArray(data)   
diff --git a/guide/example/example-formats-06.kt b/guide/example/example-formats-06.kt
index 5e4fbc5..d675df1 100644
--- a/guide/example/example-formats-06.kt
+++ b/guide/example/example-formats-06.kt
@@ -9,6 +9,7 @@
         "{${it.toUByte().toString(16).padStart(2, '0').uppercase()}}"
 }
 
+@OptIn(ExperimentalSerializationApi::class)
 @Serializable
 class Data(
     @ProtoType(ProtoIntegerType.DEFAULT)
@@ -19,6 +20,7 @@
     val c: Int
 )
 
+@OptIn(ExperimentalSerializationApi::class)
 fun main() {
     val data = Data(1, -2, 3) 
     println(ProtoBuf.encodeToByteArray(data).toAsciiHexString())
diff --git a/guide/example/example-formats-07.kt b/guide/example/example-formats-07.kt
index 52bc826..c219b8d 100644
--- a/guide/example/example-formats-07.kt
+++ b/guide/example/example-formats-07.kt
@@ -15,6 +15,7 @@
     val b: List<Int> = emptyList()
 )
 
+@OptIn(ExperimentalSerializationApi::class)
 fun main() {
     val data = Data(listOf(1, 2, 3), listOf())
     val bytes = ProtoBuf.encodeToByteArray(data)
diff --git a/guide/example/example-formats-08.kt b/guide/example/example-formats-08.kt
index 61d4aeb..23f3ccf 100644
--- a/guide/example/example-formats-08.kt
+++ b/guide/example/example-formats-08.kt
@@ -3,16 +3,34 @@
 
 import kotlinx.serialization.*
 import kotlinx.serialization.protobuf.*
-import kotlinx.serialization.protobuf.schema.ProtoBufSchemaGenerator
 
+// The outer class
+@OptIn(ExperimentalSerializationApi::class)
 @Serializable
-data class SampleData(
-    val amount: Long,
-    val description: String?,
-    val department: String = "QA"
+data class Data(
+    @ProtoNumber(1) val name: String,
+    @ProtoOneOf val phone: IPhoneType?,
 )
+
+// The oneof interface
+@Serializable sealed interface IPhoneType
+
+// Message holder for home_phone
+@OptIn(ExperimentalSerializationApi::class)
+@Serializable @JvmInline value class HomePhone(@ProtoNumber(2) val number: String): IPhoneType
+
+// Message holder for work_phone. Can also be a value class, but we leave it as `data` to demonstrate that both variants can be used.
+@OptIn(ExperimentalSerializationApi::class)
+@Serializable data class WorkPhone(@ProtoNumber(3) val number: String): IPhoneType
+
+@OptIn(ExperimentalSerializationApi::class)
 fun main() {
-  val descriptors = listOf(SampleData.serializer().descriptor)
-  val schemas = ProtoBufSchemaGenerator.generateSchemaText(descriptors)
-  println(schemas)
+  val dataTom = Data("Tom", HomePhone("123"))
+  val stringTom = ProtoBuf.encodeToHexString(dataTom)
+  val dataJerry = Data("Jerry", WorkPhone("789"))
+  val stringJerry = ProtoBuf.encodeToHexString(dataJerry)
+  println(stringTom)
+  println(stringJerry)
+  println(ProtoBuf.decodeFromHexString<Data>(stringTom))
+  println(ProtoBuf.decodeFromHexString<Data>(stringJerry))
 }
diff --git a/guide/example/example-formats-09.kt b/guide/example/example-formats-09.kt
index 99ed45d..8c1ee22 100644
--- a/guide/example/example-formats-09.kt
+++ b/guide/example/example-formats-09.kt
@@ -2,17 +2,19 @@
 package example.exampleFormats09
 
 import kotlinx.serialization.*
-import kotlinx.serialization.properties.Properties // todo: remove when no longer needed
-import kotlinx.serialization.properties.*
+import kotlinx.serialization.protobuf.*
+import kotlinx.serialization.protobuf.schema.ProtoBufSchemaGenerator
 
 @Serializable
-class Project(val name: String, val owner: User)
+data class SampleData(
+    val amount: Long,
+    val description: String?,
+    val department: String = "QA"
+)
 
-@Serializable
-class User(val name: String)
-
+@OptIn(ExperimentalSerializationApi::class)
 fun main() {
-    val data = Project("kotlinx.serialization",  User("kotlin"))
-    val map = Properties.encodeToMap(data)
-    map.forEach { (k, v) -> println("$k = $v") }
+  val descriptors = listOf(SampleData.serializer().descriptor)
+  val schemas = ProtoBufSchemaGenerator.generateSchemaText(descriptors)
+  println(schemas)
 }
diff --git a/guide/example/example-formats-10.kt b/guide/example/example-formats-10.kt
index 0fc318b..6941e45 100644
--- a/guide/example/example-formats-10.kt
+++ b/guide/example/example-formats-10.kt
@@ -2,35 +2,18 @@
 package example.exampleFormats10
 
 import kotlinx.serialization.*
-import kotlinx.serialization.descriptors.*
-import kotlinx.serialization.encoding.*
-import kotlinx.serialization.modules.*
-
-class ListEncoder : AbstractEncoder() {
-    val list = mutableListOf<Any>()
-
-    override val serializersModule: SerializersModule = EmptySerializersModule()
-
-    override fun encodeValue(value: Any) {
-        list.add(value)
-    }
-}
-
-fun <T> encodeToList(serializer: SerializationStrategy<T>, value: T): List<Any> {
-    val encoder = ListEncoder()
-    encoder.encodeSerializableValue(serializer, value)
-    return encoder.list
-}
-
-inline fun <reified T> encodeToList(value: T) = encodeToList(serializer(), value)
+import kotlinx.serialization.properties.Properties // todo: remove when no longer needed
+import kotlinx.serialization.properties.*
 
 @Serializable
-data class Project(val name: String, val owner: User, val votes: Int)
+class Project(val name: String, val owner: User)
 
 @Serializable
-data class User(val name: String)
+class User(val name: String)
 
+@OptIn(ExperimentalSerializationApi::class)
 fun main() {
-    val data = Project("kotlinx.serialization",  User("kotlin"), 9000)
-    println(encodeToList(data))
+    val data = Project("kotlinx.serialization",  User("kotlin"))
+    val map = Properties.encodeToMap(data)
+    map.forEach { (k, v) -> println("$k = $v") }
 }
diff --git a/guide/example/example-formats-11.kt b/guide/example/example-formats-11.kt
index 942febb..6b8ccd1 100644
--- a/guide/example/example-formats-11.kt
+++ b/guide/example/example-formats-11.kt
@@ -6,6 +6,7 @@
 import kotlinx.serialization.encoding.*
 import kotlinx.serialization.modules.*
 
+@ExperimentalSerializationApi
 class ListEncoder : AbstractEncoder() {
     val list = mutableListOf<Any>()
 
@@ -16,47 +17,24 @@
     }
 }
 
+@ExperimentalSerializationApi
 fun <T> encodeToList(serializer: SerializationStrategy<T>, value: T): List<Any> {
     val encoder = ListEncoder()
     encoder.encodeSerializableValue(serializer, value)
     return encoder.list
 }
 
+@ExperimentalSerializationApi
 inline fun <reified T> encodeToList(value: T) = encodeToList(serializer(), value)
 
-class ListDecoder(val list: ArrayDeque<Any>) : AbstractDecoder() {
-    private var elementIndex = 0
-
-    override val serializersModule: SerializersModule = EmptySerializersModule()
-
-    override fun decodeValue(): Any = list.removeFirst()
-    
-    override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
-        if (elementIndex == descriptor.elementsCount) return CompositeDecoder.DECODE_DONE
-        return elementIndex++
-    }
-
-    override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder =
-        ListDecoder(list)
-}
-
-fun <T> decodeFromList(list: List<Any>, deserializer: DeserializationStrategy<T>): T {
-    val decoder = ListDecoder(ArrayDeque(list))
-    return decoder.decodeSerializableValue(deserializer)
-}
-
-inline fun <reified T> decodeFromList(list: List<Any>): T = decodeFromList(list, serializer())
-
 @Serializable
 data class Project(val name: String, val owner: User, val votes: Int)
 
 @Serializable
 data class User(val name: String)
 
+@OptIn(ExperimentalSerializationApi::class)
 fun main() {
     val data = Project("kotlinx.serialization",  User("kotlin"), 9000)
-    val list = encodeToList(data)
-    println(list)
-    val obj = decodeFromList<Project>(list)
-    println(obj)
+    println(encodeToList(data))
 }
diff --git a/guide/example/example-formats-12.kt b/guide/example/example-formats-12.kt
index 1e83b9b..5f087d9 100644
--- a/guide/example/example-formats-12.kt
+++ b/guide/example/example-formats-12.kt
@@ -6,6 +6,7 @@
 import kotlinx.serialization.encoding.*
 import kotlinx.serialization.modules.*
 
+@ExperimentalSerializationApi
 class ListEncoder : AbstractEncoder() {
     val list = mutableListOf<Any>()
 
@@ -16,14 +17,17 @@
     }
 }
 
+@ExperimentalSerializationApi
 fun <T> encodeToList(serializer: SerializationStrategy<T>, value: T): List<Any> {
     val encoder = ListEncoder()
     encoder.encodeSerializableValue(serializer, value)
     return encoder.list
 }
 
+@ExperimentalSerializationApi
 inline fun <reified T> encodeToList(value: T) = encodeToList(serializer(), value)
 
+@ExperimentalSerializationApi
 class ListDecoder(val list: ArrayDeque<Any>) : AbstractDecoder() {
     private var elementIndex = 0
 
@@ -37,16 +41,16 @@
     }
 
     override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder =
-        ListDecoder(list) 
+        ListDecoder(list)
+}
 
-    override fun decodeSequentially(): Boolean = true
-}        
-
+@ExperimentalSerializationApi
 fun <T> decodeFromList(list: List<Any>, deserializer: DeserializationStrategy<T>): T {
     val decoder = ListDecoder(ArrayDeque(list))
     return decoder.decodeSerializableValue(deserializer)
 }
 
+@ExperimentalSerializationApi
 inline fun <reified T> decodeFromList(list: List<Any>): T = decodeFromList(list, serializer())
 
 @Serializable
@@ -55,6 +59,7 @@
 @Serializable
 data class User(val name: String)
 
+@OptIn(ExperimentalSerializationApi::class)
 fun main() {
     val data = Project("kotlinx.serialization",  User("kotlin"), 9000)
     val list = encodeToList(data)
diff --git a/guide/example/example-formats-13.kt b/guide/example/example-formats-13.kt
index 62ecdc6..f2915f6 100644
--- a/guide/example/example-formats-13.kt
+++ b/guide/example/example-formats-13.kt
@@ -6,6 +6,7 @@
 import kotlinx.serialization.encoding.*
 import kotlinx.serialization.modules.*
 
+@ExperimentalSerializationApi
 class ListEncoder : AbstractEncoder() {
     val list = mutableListOf<Any>()
 
@@ -13,58 +14,56 @@
 
     override fun encodeValue(value: Any) {
         list.add(value)
-    }                               
-
-    override fun beginCollection(descriptor: SerialDescriptor, collectionSize: Int): CompositeEncoder {
-        encodeInt(collectionSize)
-        return this
-    }                                                
+    }
 }
 
+@ExperimentalSerializationApi
 fun <T> encodeToList(serializer: SerializationStrategy<T>, value: T): List<Any> {
     val encoder = ListEncoder()
     encoder.encodeSerializableValue(serializer, value)
     return encoder.list
 }
 
+@ExperimentalSerializationApi
 inline fun <reified T> encodeToList(value: T) = encodeToList(serializer(), value)
 
-class ListDecoder(val list: ArrayDeque<Any>, var elementsCount: Int = 0) : AbstractDecoder() {
+@ExperimentalSerializationApi
+class ListDecoder(val list: ArrayDeque<Any>) : AbstractDecoder() {
     private var elementIndex = 0
 
     override val serializersModule: SerializersModule = EmptySerializersModule()
 
     override fun decodeValue(): Any = list.removeFirst()
-
+    
     override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
-        if (elementIndex == elementsCount) return CompositeDecoder.DECODE_DONE
+        if (elementIndex == descriptor.elementsCount) return CompositeDecoder.DECODE_DONE
         return elementIndex++
     }
 
     override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder =
-        ListDecoder(list, descriptor.elementsCount)
+        ListDecoder(list) 
 
     override fun decodeSequentially(): Boolean = true
+}        
 
-    override fun decodeCollectionSize(descriptor: SerialDescriptor): Int =
-        decodeInt().also { elementsCount = it }
-}
-
+@ExperimentalSerializationApi
 fun <T> decodeFromList(list: List<Any>, deserializer: DeserializationStrategy<T>): T {
     val decoder = ListDecoder(ArrayDeque(list))
     return decoder.decodeSerializableValue(deserializer)
 }
 
+@ExperimentalSerializationApi
 inline fun <reified T> decodeFromList(list: List<Any>): T = decodeFromList(list, serializer())
 
 @Serializable
-data class Project(val name: String, val owners: List<User>, val votes: Int)
+data class Project(val name: String, val owner: User, val votes: Int)
 
 @Serializable
 data class User(val name: String)
 
+@OptIn(ExperimentalSerializationApi::class)
 fun main() {
-    val data = Project("kotlinx.serialization",  listOf(User("kotlin"), User("jetbrains")), 9000)
+    val data = Project("kotlinx.serialization",  User("kotlin"), 9000)
     val list = encodeToList(data)
     println(list)
     val obj = decodeFromList<Project>(list)
diff --git a/guide/example/example-formats-14.kt b/guide/example/example-formats-14.kt
index cd823e8..782022a 100644
--- a/guide/example/example-formats-14.kt
+++ b/guide/example/example-formats-14.kt
@@ -6,6 +6,7 @@
 import kotlinx.serialization.encoding.*
 import kotlinx.serialization.modules.*
 
+@ExperimentalSerializationApi
 class ListEncoder : AbstractEncoder() {
     val list = mutableListOf<Any>()
 
@@ -19,22 +20,22 @@
         encodeInt(collectionSize)
         return this
     }                                                
-
-    override fun encodeNull() = encodeValue("NULL")
-    override fun encodeNotNullMark() = encodeValue("!!")
 }
 
+@ExperimentalSerializationApi
 fun <T> encodeToList(serializer: SerializationStrategy<T>, value: T): List<Any> {
     val encoder = ListEncoder()
     encoder.encodeSerializableValue(serializer, value)
     return encoder.list
 }
 
+@ExperimentalSerializationApi
 inline fun <reified T> encodeToList(value: T) = encodeToList(serializer(), value)
 
+@ExperimentalSerializationApi
 class ListDecoder(val list: ArrayDeque<Any>, var elementsCount: Int = 0) : AbstractDecoder() {
     private var elementIndex = 0
-    
+
     override val serializersModule: SerializersModule = EmptySerializersModule()
 
     override fun decodeValue(): Any = list.removeFirst()
@@ -51,28 +52,28 @@
 
     override fun decodeCollectionSize(descriptor: SerialDescriptor): Int =
         decodeInt().also { elementsCount = it }
-
-    override fun decodeNotNullMark(): Boolean = decodeString() != "NULL"
 }
 
+@ExperimentalSerializationApi
 fun <T> decodeFromList(list: List<Any>, deserializer: DeserializationStrategy<T>): T {
     val decoder = ListDecoder(ArrayDeque(list))
     return decoder.decodeSerializableValue(deserializer)
 }
 
+@ExperimentalSerializationApi
 inline fun <reified T> decodeFromList(list: List<Any>): T = decodeFromList(list, serializer())
 
 @Serializable
-data class Project(val name: String, val owner: User?, val votes: Int?)
+data class Project(val name: String, val owners: List<User>, val votes: Int)
 
 @Serializable
 data class User(val name: String)
 
+@OptIn(ExperimentalSerializationApi::class)
 fun main() {
-    val data = Project("kotlinx.serialization",  User("kotlin") , null)
+    val data = Project("kotlinx.serialization",  listOf(User("kotlin"), User("jetbrains")), 9000)
     val list = encodeToList(data)
     println(list)
     val obj = decodeFromList<Project>(list)
     println(obj)
 }
-
diff --git a/guide/example/example-formats-15.kt b/guide/example/example-formats-15.kt
index 81928d4..354ea1c 100644
--- a/guide/example/example-formats-15.kt
+++ b/guide/example/example-formats-15.kt
@@ -2,54 +2,46 @@
 package example.exampleFormats15
 
 import kotlinx.serialization.*
-import kotlinx.serialization.Serializable
 import kotlinx.serialization.descriptors.*
 import kotlinx.serialization.encoding.*
 import kotlinx.serialization.modules.*
-import java.io.*
 
-class DataOutputEncoder(val output: DataOutput) : AbstractEncoder() {
+@ExperimentalSerializationApi
+class ListEncoder : AbstractEncoder() {
+    val list = mutableListOf<Any>()
+
     override val serializersModule: SerializersModule = EmptySerializersModule()
-    override fun encodeBoolean(value: Boolean) = output.writeByte(if (value) 1 else 0)
-    override fun encodeByte(value: Byte) = output.writeByte(value.toInt())
-    override fun encodeShort(value: Short) = output.writeShort(value.toInt())
-    override fun encodeInt(value: Int) = output.writeInt(value)
-    override fun encodeLong(value: Long) = output.writeLong(value)
-    override fun encodeFloat(value: Float) = output.writeFloat(value)
-    override fun encodeDouble(value: Double) = output.writeDouble(value)
-    override fun encodeChar(value: Char) = output.writeChar(value.code)
-    override fun encodeString(value: String) = output.writeUTF(value)
-    override fun encodeEnum(enumDescriptor: SerialDescriptor, index: Int) = output.writeInt(index)
+
+    override fun encodeValue(value: Any) {
+        list.add(value)
+    }                               
 
     override fun beginCollection(descriptor: SerialDescriptor, collectionSize: Int): CompositeEncoder {
         encodeInt(collectionSize)
         return this
-    }
+    }                                                
 
-    override fun encodeNull() = encodeBoolean(false)
-    override fun encodeNotNullMark() = encodeBoolean(true)
+    override fun encodeNull() = encodeValue("NULL")
+    override fun encodeNotNullMark() = encodeValue("!!")
 }
 
-fun <T> encodeTo(output: DataOutput, serializer: SerializationStrategy<T>, value: T) {
-    val encoder = DataOutputEncoder(output)
+@ExperimentalSerializationApi
+fun <T> encodeToList(serializer: SerializationStrategy<T>, value: T): List<Any> {
+    val encoder = ListEncoder()
     encoder.encodeSerializableValue(serializer, value)
+    return encoder.list
 }
 
-inline fun <reified T> encodeTo(output: DataOutput, value: T) = encodeTo(output, serializer(), value)
+@ExperimentalSerializationApi
+inline fun <reified T> encodeToList(value: T) = encodeToList(serializer(), value)
 
-class DataInputDecoder(val input: DataInput, var elementsCount: Int = 0) : AbstractDecoder() {
+@ExperimentalSerializationApi
+class ListDecoder(val list: ArrayDeque<Any>, var elementsCount: Int = 0) : AbstractDecoder() {
     private var elementIndex = 0
+    
     override val serializersModule: SerializersModule = EmptySerializersModule()
-    override fun decodeBoolean(): Boolean = input.readByte().toInt() != 0
-    override fun decodeByte(): Byte = input.readByte()
-    override fun decodeShort(): Short = input.readShort()
-    override fun decodeInt(): Int = input.readInt()
-    override fun decodeLong(): Long = input.readLong()
-    override fun decodeFloat(): Float = input.readFloat()
-    override fun decodeDouble(): Double = input.readDouble()
-    override fun decodeChar(): Char = input.readChar()
-    override fun decodeString(): String = input.readUTF()
-    override fun decodeEnum(enumDescriptor: SerialDescriptor): Int = input.readInt()
+
+    override fun decodeValue(): Any = list.removeFirst()
 
     override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
         if (elementIndex == elementsCount) return CompositeDecoder.DECODE_DONE
@@ -57,38 +49,37 @@
     }
 
     override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder =
-        DataInputDecoder(input, descriptor.elementsCount)
+        ListDecoder(list, descriptor.elementsCount)
 
     override fun decodeSequentially(): Boolean = true
 
     override fun decodeCollectionSize(descriptor: SerialDescriptor): Int =
         decodeInt().also { elementsCount = it }
 
-    override fun decodeNotNullMark(): Boolean = decodeBoolean()
+    override fun decodeNotNullMark(): Boolean = decodeString() != "NULL"
 }
 
-fun <T> decodeFrom(input: DataInput, deserializer: DeserializationStrategy<T>): T {
-    val decoder = DataInputDecoder(input)
+@ExperimentalSerializationApi
+fun <T> decodeFromList(list: List<Any>, deserializer: DeserializationStrategy<T>): T {
+    val decoder = ListDecoder(ArrayDeque(list))
     return decoder.decodeSerializableValue(deserializer)
 }
 
-inline fun <reified T> decodeFrom(input: DataInput): T = decodeFrom(input, serializer())
-
-fun ByteArray.toAsciiHexString() = joinToString("") {
-    if (it in 32..127) it.toInt().toChar().toString() else
-        "{${it.toUByte().toString(16).padStart(2, '0').uppercase()}}"
-}
+@ExperimentalSerializationApi
+inline fun <reified T> decodeFromList(list: List<Any>): T = decodeFromList(list, serializer())
 
 @Serializable
-data class Project(val name: String, val language: String)
+data class Project(val name: String, val owner: User?, val votes: Int?)
 
+@Serializable
+data class User(val name: String)
+
+@OptIn(ExperimentalSerializationApi::class)
 fun main() {
-    val data = Project("kotlinx.serialization", "Kotlin")
-    val output = ByteArrayOutputStream()
-    encodeTo(DataOutputStream(output), data)
-    val bytes = output.toByteArray()
-    println(bytes.toAsciiHexString())
-    val input = ByteArrayInputStream(bytes)
-    val obj = decodeFrom<Project>(DataInputStream(input))
+    val data = Project("kotlinx.serialization",  User("kotlin") , null)
+    val list = encodeToList(data)
+    println(list)
+    val obj = decodeFromList<Project>(list)
     println(obj)
 }
+
diff --git a/guide/example/example-formats-16.kt b/guide/example/example-formats-16.kt
index 2460490..7a93ce2 100644
--- a/guide/example/example-formats-16.kt
+++ b/guide/example/example-formats-16.kt
@@ -4,11 +4,11 @@
 import kotlinx.serialization.*
 import kotlinx.serialization.Serializable
 import kotlinx.serialization.descriptors.*
-import kotlinx.serialization.modules.*
 import kotlinx.serialization.encoding.*
+import kotlinx.serialization.modules.*
 import java.io.*
 
-private val byteArraySerializer = serializer<ByteArray>()
+@ExperimentalSerializationApi
 class DataOutputEncoder(val output: DataOutput) : AbstractEncoder() {
     override val serializersModule: SerializersModule = EmptySerializersModule()
     override fun encodeBoolean(value: Boolean) = output.writeByte(if (value) 1 else 0)
@@ -29,36 +29,18 @@
 
     override fun encodeNull() = encodeBoolean(false)
     override fun encodeNotNullMark() = encodeBoolean(true)
-
-    override fun <T> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) {
-        if (serializer.descriptor == byteArraySerializer.descriptor)
-            encodeByteArray(value as ByteArray)
-        else
-            super.encodeSerializableValue(serializer, value)
-    }
-
-    private fun encodeByteArray(bytes: ByteArray) {
-        encodeCompactSize(bytes.size)
-        output.write(bytes)
-    }
-    
-    private fun encodeCompactSize(value: Int) {
-        if (value < 0xff) {
-            output.writeByte(value)
-        } else {
-            output.writeByte(0xff)
-            output.writeInt(value)
-        }
-    }            
 }
 
+@ExperimentalSerializationApi
 fun <T> encodeTo(output: DataOutput, serializer: SerializationStrategy<T>, value: T) {
     val encoder = DataOutputEncoder(output)
     encoder.encodeSerializableValue(serializer, value)
 }
 
+@ExperimentalSerializationApi
 inline fun <reified T> encodeTo(output: DataOutput, value: T) = encodeTo(output, serializer(), value)
 
+@ExperimentalSerializationApi
 class DataInputDecoder(val input: DataInput, var elementsCount: Int = 0) : AbstractDecoder() {
     private var elementIndex = 0
     override val serializersModule: SerializersModule = EmptySerializersModule()
@@ -87,32 +69,15 @@
         decodeInt().also { elementsCount = it }
 
     override fun decodeNotNullMark(): Boolean = decodeBoolean()
-
-    @Suppress("UNCHECKED_CAST")
-    override fun <T> decodeSerializableValue(deserializer: DeserializationStrategy<T>, previousValue: T?): T =
-        if (deserializer.descriptor == byteArraySerializer.descriptor)
-            decodeByteArray() as T
-        else
-            super.decodeSerializableValue(deserializer, previousValue)
-
-    private fun decodeByteArray(): ByteArray {
-        val bytes = ByteArray(decodeCompactSize())
-        input.readFully(bytes)
-        return bytes
-    }
-
-    private fun decodeCompactSize(): Int {
-        val byte = input.readByte().toInt() and 0xff
-        if (byte < 0xff) return byte
-        return input.readInt()
-    }
 }
 
+@ExperimentalSerializationApi
 fun <T> decodeFrom(input: DataInput, deserializer: DeserializationStrategy<T>): T {
     val decoder = DataInputDecoder(input)
     return decoder.decodeSerializableValue(deserializer)
 }
 
+@ExperimentalSerializationApi
 inline fun <reified T> decodeFrom(input: DataInput): T = decodeFrom(input, serializer())
 
 fun ByteArray.toAsciiHexString() = joinToString("") {
@@ -121,10 +86,11 @@
 }
 
 @Serializable
-data class Project(val name: String, val attachment: ByteArray)
+data class Project(val name: String, val language: String)
 
+@OptIn(ExperimentalSerializationApi::class)
 fun main() {
-    val data = Project("kotlinx.serialization", byteArrayOf(0x0A, 0x0B, 0x0C, 0x0D))
+    val data = Project("kotlinx.serialization", "Kotlin")
     val output = ByteArrayOutputStream()
     encodeTo(DataOutputStream(output), data)
     val bytes = output.toByteArray()
diff --git a/guide/example/example-formats-17.kt b/guide/example/example-formats-17.kt
new file mode 100644
index 0000000..ab16bf7
--- /dev/null
+++ b/guide/example/example-formats-17.kt
@@ -0,0 +1,142 @@
+// This file was automatically generated from formats.md by Knit tool. Do not edit.
+package example.exampleFormats17
+
+import kotlinx.serialization.*
+import kotlinx.serialization.Serializable
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.modules.*
+import kotlinx.serialization.encoding.*
+import java.io.*
+
+private val byteArraySerializer = serializer<ByteArray>()
+@ExperimentalSerializationApi
+class DataOutputEncoder(val output: DataOutput) : AbstractEncoder() {
+    override val serializersModule: SerializersModule = EmptySerializersModule()
+    override fun encodeBoolean(value: Boolean) = output.writeByte(if (value) 1 else 0)
+    override fun encodeByte(value: Byte) = output.writeByte(value.toInt())
+    override fun encodeShort(value: Short) = output.writeShort(value.toInt())
+    override fun encodeInt(value: Int) = output.writeInt(value)
+    override fun encodeLong(value: Long) = output.writeLong(value)
+    override fun encodeFloat(value: Float) = output.writeFloat(value)
+    override fun encodeDouble(value: Double) = output.writeDouble(value)
+    override fun encodeChar(value: Char) = output.writeChar(value.code)
+    override fun encodeString(value: String) = output.writeUTF(value)
+    override fun encodeEnum(enumDescriptor: SerialDescriptor, index: Int) = output.writeInt(index)
+
+    override fun beginCollection(descriptor: SerialDescriptor, collectionSize: Int): CompositeEncoder {
+        encodeInt(collectionSize)
+        return this
+    }
+
+    override fun encodeNull() = encodeBoolean(false)
+    override fun encodeNotNullMark() = encodeBoolean(true)
+
+    override fun <T> encodeSerializableValue(serializer: SerializationStrategy<T>, value: T) {
+        if (serializer.descriptor == byteArraySerializer.descriptor)
+            encodeByteArray(value as ByteArray)
+        else
+            super.encodeSerializableValue(serializer, value)
+    }
+
+    private fun encodeByteArray(bytes: ByteArray) {
+        encodeCompactSize(bytes.size)
+        output.write(bytes)
+    }
+    
+    private fun encodeCompactSize(value: Int) {
+        if (value < 0xff) {
+            output.writeByte(value)
+        } else {
+            output.writeByte(0xff)
+            output.writeInt(value)
+        }
+    }            
+}
+
+@ExperimentalSerializationApi
+fun <T> encodeTo(output: DataOutput, serializer: SerializationStrategy<T>, value: T) {
+    val encoder = DataOutputEncoder(output)
+    encoder.encodeSerializableValue(serializer, value)
+}
+
+@ExperimentalSerializationApi
+inline fun <reified T> encodeTo(output: DataOutput, value: T) = encodeTo(output, serializer(), value)
+
+@ExperimentalSerializationApi
+class DataInputDecoder(val input: DataInput, var elementsCount: Int = 0) : AbstractDecoder() {
+    private var elementIndex = 0
+    override val serializersModule: SerializersModule = EmptySerializersModule()
+    override fun decodeBoolean(): Boolean = input.readByte().toInt() != 0
+    override fun decodeByte(): Byte = input.readByte()
+    override fun decodeShort(): Short = input.readShort()
+    override fun decodeInt(): Int = input.readInt()
+    override fun decodeLong(): Long = input.readLong()
+    override fun decodeFloat(): Float = input.readFloat()
+    override fun decodeDouble(): Double = input.readDouble()
+    override fun decodeChar(): Char = input.readChar()
+    override fun decodeString(): String = input.readUTF()
+    override fun decodeEnum(enumDescriptor: SerialDescriptor): Int = input.readInt()
+
+    override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
+        if (elementIndex == elementsCount) return CompositeDecoder.DECODE_DONE
+        return elementIndex++
+    }
+
+    override fun beginStructure(descriptor: SerialDescriptor): CompositeDecoder =
+        DataInputDecoder(input, descriptor.elementsCount)
+
+    override fun decodeSequentially(): Boolean = true
+
+    override fun decodeCollectionSize(descriptor: SerialDescriptor): Int =
+        decodeInt().also { elementsCount = it }
+
+    override fun decodeNotNullMark(): Boolean = decodeBoolean()
+
+    @Suppress("UNCHECKED_CAST")
+    override fun <T> decodeSerializableValue(deserializer: DeserializationStrategy<T>, previousValue: T?): T =
+        if (deserializer.descriptor == byteArraySerializer.descriptor)
+            decodeByteArray() as T
+        else
+            super.decodeSerializableValue(deserializer, previousValue)
+
+    private fun decodeByteArray(): ByteArray {
+        val bytes = ByteArray(decodeCompactSize())
+        input.readFully(bytes)
+        return bytes
+    }
+
+    private fun decodeCompactSize(): Int {
+        val byte = input.readByte().toInt() and 0xff
+        if (byte < 0xff) return byte
+        return input.readInt()
+    }
+}
+
+@ExperimentalSerializationApi
+fun <T> decodeFrom(input: DataInput, deserializer: DeserializationStrategy<T>): T {
+    val decoder = DataInputDecoder(input)
+    return decoder.decodeSerializableValue(deserializer)
+}
+
+@ExperimentalSerializationApi
+inline fun <reified T> decodeFrom(input: DataInput): T = decodeFrom(input, serializer())
+
+fun ByteArray.toAsciiHexString() = joinToString("") {
+    if (it in 32..127) it.toInt().toChar().toString() else
+        "{${it.toUByte().toString(16).padStart(2, '0').uppercase()}}"
+}
+
+@Serializable
+data class Project(val name: String, val attachment: ByteArray)
+
+@OptIn(ExperimentalSerializationApi::class)
+fun main() {
+    val data = Project("kotlinx.serialization", byteArrayOf(0x0A, 0x0B, 0x0C, 0x0D))
+    val output = ByteArrayOutputStream()
+    encodeTo(DataOutputStream(output), data)
+    val bytes = output.toByteArray()
+    println(bytes.toAsciiHexString())
+    val input = ByteArrayInputStream(bytes)
+    val obj = decodeFrom<Project>(DataInputStream(input))
+    println(obj)
+}
diff --git a/guide/example/example-json-04.kt b/guide/example/example-json-04.kt
index a8ae148..92d0367 100644
--- a/guide/example/example-json-04.kt
+++ b/guide/example/example-json-04.kt
@@ -4,6 +4,7 @@
 import kotlinx.serialization.*
 import kotlinx.serialization.json.*
 
+@OptIn(ExperimentalSerializationApi::class) // JsonNames is an experimental annotation for now
 @Serializable
 data class Project(@JsonNames("title") val name: String)
 
diff --git a/guide/example/example-json-05.kt b/guide/example/example-json-05.kt
index e1b5422..809cc9e 100644
--- a/guide/example/example-json-05.kt
+++ b/guide/example/example-json-05.kt
@@ -4,14 +4,16 @@
 import kotlinx.serialization.*
 import kotlinx.serialization.json.*
 
-val format = Json { coerceInputValues = true }
+val format = Json { encodeDefaults = true }
 
 @Serializable
-data class Project(val name: String, val language: String = "Kotlin")
+class Project(
+    val name: String,
+    val language: String = "Kotlin",
+    val website: String? = null
+)
 
 fun main() {
-    val data = format.decodeFromString<Project>("""
-        {"name":"kotlinx.serialization","language":null}
-    """)
-    println(data)
+    val data = Project("kotlinx.serialization")
+    println(format.encodeToString(data))
 }
diff --git a/guide/example/example-json-06.kt b/guide/example/example-json-06.kt
index 605b488..776e3ec 100644
--- a/guide/example/example-json-06.kt
+++ b/guide/example/example-json-06.kt
@@ -4,16 +4,20 @@
 import kotlinx.serialization.*
 import kotlinx.serialization.json.*
 
-val format = Json { encodeDefaults = true }
+val format = Json { explicitNulls = false }
 
 @Serializable
-class Project(
+data class Project(
     val name: String,
-    val language: String = "Kotlin",
-    val website: String? = null
+    val language: String,
+    val version: String? = "1.2.2",
+    val website: String?,
+    val description: String? = null
 )
 
 fun main() {
-    val data = Project("kotlinx.serialization")
-    println(format.encodeToString(data))
+    val data = Project("kotlinx.serialization", "Kotlin", null, null, null)
+    val json = format.encodeToString(data)
+    println(json)
+    println(format.decodeFromString<Project>(json))
 }
diff --git a/guide/example/example-json-07.kt b/guide/example/example-json-07.kt
index 60aa2b2..4d9ad2c 100644
--- a/guide/example/example-json-07.kt
+++ b/guide/example/example-json-07.kt
@@ -4,20 +4,14 @@
 import kotlinx.serialization.*
 import kotlinx.serialization.json.*
 
-val format = Json { explicitNulls = false }
+val format = Json { coerceInputValues = true }
 
 @Serializable
-data class Project(
-    val name: String,
-    val language: String,
-    val version: String? = "1.2.2",
-    val website: String?,
-    val description: String? = null
-)
+data class Project(val name: String, val language: String = "Kotlin")
 
 fun main() {
-    val data = Project("kotlinx.serialization", "Kotlin", null, null, null)
-    val json = format.encodeToString(data)
-    println(json)
-    println(format.decodeFromString<Project>(json))
+    val data = format.decodeFromString<Project>("""
+        {"name":"kotlinx.serialization","language":null}
+    """)
+    println(data)
 }
diff --git a/guide/example/example-json-08.kt b/guide/example/example-json-08.kt
index 86e6298..501a38e 100644
--- a/guide/example/example-json-08.kt
+++ b/guide/example/example-json-08.kt
@@ -4,15 +4,17 @@
 import kotlinx.serialization.*
 import kotlinx.serialization.json.*
 
-val format = Json { allowStructuredMapKeys = true }
+enum class Color { BLACK, WHITE }
 
 @Serializable
-data class Project(val name: String)
+data class Brush(val foreground: Color = Color.BLACK, val background: Color?)
+
+val json = Json { 
+  coerceInputValues = true
+  explicitNulls = false
+}
 
 fun main() {
-    val map = mapOf(
-        Project("kotlinx.serialization") to "Serialization",
-        Project("kotlinx.coroutines") to "Coroutines"
-    )
-    println(format.encodeToString(map))
+    val brush = json.decodeFromString<Brush>("""{"foreground":"pink", "background":"purple"}""")
+  println(brush)
 }
diff --git a/guide/example/example-json-09.kt b/guide/example/example-json-09.kt
index 1303fdd..a0ed632 100644
--- a/guide/example/example-json-09.kt
+++ b/guide/example/example-json-09.kt
@@ -4,14 +4,15 @@
 import kotlinx.serialization.*
 import kotlinx.serialization.json.*
 
-val format = Json { allowSpecialFloatingPointValues = true }
+val format = Json { allowStructuredMapKeys = true }
 
 @Serializable
-class Data(
-    val value: Double
-)
+data class Project(val name: String)
 
 fun main() {
-    val data = Data(Double.NaN)
-    println(format.encodeToString(data))
+    val map = mapOf(
+        Project("kotlinx.serialization") to "Serialization",
+        Project("kotlinx.coroutines") to "Coroutines"
+    )
+    println(format.encodeToString(map))
 }
diff --git a/guide/example/example-json-10.kt b/guide/example/example-json-10.kt
index 49df395..dc528bb 100644
--- a/guide/example/example-json-10.kt
+++ b/guide/example/example-json-10.kt
@@ -4,18 +4,14 @@
 import kotlinx.serialization.*
 import kotlinx.serialization.json.*
 
-val format = Json { classDiscriminator = "#class" }
+val format = Json { allowSpecialFloatingPointValues = true }
 
 @Serializable
-sealed class Project {
-    abstract val name: String
-}
-
-@Serializable
-@SerialName("owned")
-class OwnedProject(override val name: String, val owner: String) : Project()
+class Data(
+    val value: Double
+)
 
 fun main() {
-    val data: Project = OwnedProject("kotlinx.coroutines", "kotlin")
+    val data = Data(Double.NaN)
     println(format.encodeToString(data))
 }
diff --git a/guide/example/example-json-11.kt b/guide/example/example-json-11.kt
index 57e350a..31f8731 100644
--- a/guide/example/example-json-11.kt
+++ b/guide/example/example-json-11.kt
@@ -4,28 +4,18 @@
 import kotlinx.serialization.*
 import kotlinx.serialization.json.*
 
-@Serializable
-@JsonClassDiscriminator("message_type")
-sealed class Base
-
-@Serializable // Class discriminator is inherited from Base
-sealed class ErrorClass: Base()
-
-@Serializable
-data class Message(val message: Base, val error: ErrorClass?)
-
-@Serializable
-@SerialName("my.app.BaseMessage")
-data class BaseMessage(val message: String) : Base()
-
-@Serializable
-@SerialName("my.app.GenericError")
-data class GenericError(@SerialName("error_code") val errorCode: Int) : ErrorClass()
-
-
 val format = Json { classDiscriminator = "#class" }
 
+@Serializable
+sealed class Project {
+    abstract val name: String
+}
+
+@Serializable
+@SerialName("owned")
+class OwnedProject(override val name: String, val owner: String) : Project()
+
 fun main() {
-    val data = Message(BaseMessage("not found"), GenericError(404))
+    val data: Project = OwnedProject("kotlinx.coroutines", "kotlin")
     println(format.encodeToString(data))
 }
diff --git a/guide/example/example-json-12.kt b/guide/example/example-json-12.kt
index 99a872b..f3f11a6 100644
--- a/guide/example/example-json-12.kt
+++ b/guide/example/example-json-12.kt
@@ -4,17 +4,29 @@
 import kotlinx.serialization.*
 import kotlinx.serialization.json.*
 
-val format = Json { classDiscriminatorMode = ClassDiscriminatorMode.NONE }
+@OptIn(ExperimentalSerializationApi::class) // JsonClassDiscriminator is an experimental annotation for now
+@Serializable
+@JsonClassDiscriminator("message_type")
+sealed class Base
+
+@Serializable // Class discriminator is inherited from Base
+sealed class ErrorClass: Base()
 
 @Serializable
-sealed class Project {
-    abstract val name: String
-}
+data class Message(val message: Base, val error: ErrorClass?)
 
 @Serializable
-class OwnedProject(override val name: String, val owner: String) : Project()
+@SerialName("my.app.BaseMessage")
+data class BaseMessage(val message: String) : Base()
+
+@Serializable
+@SerialName("my.app.GenericError")
+data class GenericError(@SerialName("error_code") val errorCode: Int) : ErrorClass()
+
+
+val format = Json { classDiscriminator = "#class" }
 
 fun main() {
-    val data: Project = OwnedProject("kotlinx.coroutines", "kotlin")
+    val data = Message(BaseMessage("not found"), GenericError(404))
     println(format.encodeToString(data))
 }
diff --git a/guide/example/example-json-13.kt b/guide/example/example-json-13.kt
index e20afe2..9794230 100644
--- a/guide/example/example-json-13.kt
+++ b/guide/example/example-json-13.kt
@@ -4,13 +4,18 @@
 import kotlinx.serialization.*
 import kotlinx.serialization.json.*
 
-val format = Json { decodeEnumsCaseInsensitive = true }
-
-enum class Cases { VALUE_A, @JsonNames("Alternative") VALUE_B }
+@OptIn(ExperimentalSerializationApi::class) // classDiscriminatorMode is an experimental setting for now
+val format = Json { classDiscriminatorMode = ClassDiscriminatorMode.NONE }
 
 @Serializable
-data class CasesList(val cases: List<Cases>)
+sealed class Project {
+    abstract val name: String
+}
+
+@Serializable
+class OwnedProject(override val name: String, val owner: String) : Project()
 
 fun main() {
-  println(format.decodeFromString<CasesList>("""{"cases":["value_A", "alternative"]}""")) 
+    val data: Project = OwnedProject("kotlinx.coroutines", "kotlin")
+    println(format.encodeToString(data))
 }
diff --git a/guide/example/example-json-14.kt b/guide/example/example-json-14.kt
index 50de55f..f0def0e 100644
--- a/guide/example/example-json-14.kt
+++ b/guide/example/example-json-14.kt
@@ -4,12 +4,15 @@
 import kotlinx.serialization.*
 import kotlinx.serialization.json.*
 
-@Serializable
-data class Project(val projectName: String, val projectOwner: String)
+@OptIn(ExperimentalSerializationApi::class) // decodeEnumsCaseInsensitive is an experimental setting for now
+val format = Json { decodeEnumsCaseInsensitive = true }
 
-val format = Json { namingStrategy = JsonNamingStrategy.SnakeCase }
+@OptIn(ExperimentalSerializationApi::class) // JsonNames is an experimental annotation for now
+enum class Cases { VALUE_A, @JsonNames("Alternative") VALUE_B }
+
+@Serializable
+data class CasesList(val cases: List<Cases>)
 
 fun main() {
-    val project = format.decodeFromString<Project>("""{"project_name":"kotlinx.coroutines", "project_owner":"Kotlin"}""")
-    println(format.encodeToString(project.copy(projectName = "kotlinx.serialization")))
+  println(format.decodeFromString<CasesList>("""{"cases":["value_A", "alternative"]}""")) 
 }
diff --git a/guide/example/example-json-15.kt b/guide/example/example-json-15.kt
index 384ae41..267d5cc 100644
--- a/guide/example/example-json-15.kt
+++ b/guide/example/example-json-15.kt
@@ -4,9 +4,13 @@
 import kotlinx.serialization.*
 import kotlinx.serialization.json.*
 
+@Serializable
+data class Project(val projectName: String, val projectOwner: String)
+
+@OptIn(ExperimentalSerializationApi::class) // namingStrategy is an experimental setting for now
+val format = Json { namingStrategy = JsonNamingStrategy.SnakeCase }
+
 fun main() {
-    val element = Json.parseToJsonElement("""
-        {"name":"kotlinx.serialization","language":"Kotlin"}
-    """)
-    println(element)
+    val project = format.decodeFromString<Project>("""{"project_name":"kotlinx.coroutines", "project_owner":"Kotlin"}""")
+    println(format.encodeToString(project.copy(projectName = "kotlinx.serialization")))
 }
diff --git a/guide/example/example-json-16.kt b/guide/example/example-json-16.kt
index fff287a..eaa7c90 100644
--- a/guide/example/example-json-16.kt
+++ b/guide/example/example-json-16.kt
@@ -4,15 +4,54 @@
 import kotlinx.serialization.*
 import kotlinx.serialization.json.*
 
+import kotlinx.serialization.encoding.Encoder
+import kotlinx.serialization.encoding.Decoder
+import kotlinx.serialization.descriptors.*
+import kotlin.io.encoding.*
+
+@OptIn(ExperimentalEncodingApi::class)
+object ByteArrayAsBase64Serializer : KSerializer<ByteArray> {
+    private val base64 = Base64.Default
+
+    override val descriptor: SerialDescriptor
+        get() = PrimitiveSerialDescriptor(
+            "ByteArrayAsBase64Serializer",
+            PrimitiveKind.STRING
+        )
+
+    override fun serialize(encoder: Encoder, value: ByteArray) {
+        val base64Encoded = base64.encode(value)
+        encoder.encodeString(base64Encoded)
+    }
+
+    override fun deserialize(decoder: Decoder): ByteArray {
+        val base64Decoded = decoder.decodeString()
+        return base64.decode(base64Decoded)
+    }
+}
+
+@Serializable
+data class Value(
+    @Serializable(with = ByteArrayAsBase64Serializer::class)
+    val base64Input: ByteArray
+) {
+    override fun equals(other: Any?): Boolean {
+        if (this === other) return true
+        if (javaClass != other?.javaClass) return false
+        other as Value
+        return base64Input.contentEquals(other.base64Input)
+    }
+
+    override fun hashCode(): Int {
+        return base64Input.contentHashCode()
+    }
+}
+
 fun main() {
-    val element = Json.parseToJsonElement("""
-        {
-            "name": "kotlinx.serialization",
-            "forks": [{"votes": 42}, {"votes": 9000}, {}]
-        }
-    """)
-    val sum = element
-        .jsonObject["forks"]!!
-        .jsonArray.sumOf { it.jsonObject["votes"]?.jsonPrimitive?.int ?: 0 }
-    println(sum)
+    val string = "foo string"
+    val value = Value(string.toByteArray())
+    val encoded = Json.encodeToString(value)
+    println(encoded)
+    val decoded = Json.decodeFromString<Value>(encoded)
+    println(decoded.base64Input.decodeToString())
 }
diff --git a/guide/example/example-json-17.kt b/guide/example/example-json-17.kt
index 72a696a..ba7177d 100644
--- a/guide/example/example-json-17.kt
+++ b/guide/example/example-json-17.kt
@@ -5,19 +5,8 @@
 import kotlinx.serialization.json.*
 
 fun main() {
-    val element = buildJsonObject {
-        put("name", "kotlinx.serialization")
-        putJsonObject("owner") {
-            put("name", "kotlin")
-        }
-        putJsonArray("forks") {
-            addJsonObject {
-                put("votes", 42)
-            }
-            addJsonObject {
-                put("votes", 9000)
-            }
-        }
-    }
+    val element = Json.parseToJsonElement("""
+        {"name":"kotlinx.serialization","language":"Kotlin"}
+    """)
     println(element)
 }
diff --git a/guide/example/example-json-18.kt b/guide/example/example-json-18.kt
index 1b655bf..f378615 100644
--- a/guide/example/example-json-18.kt
+++ b/guide/example/example-json-18.kt
@@ -4,14 +4,15 @@
 import kotlinx.serialization.*
 import kotlinx.serialization.json.*
 
-@Serializable
-data class Project(val name: String, val language: String)
-
 fun main() {
-    val element = buildJsonObject {
-        put("name", "kotlinx.serialization")
-        put("language", "Kotlin")
-    }
-    val data = Json.decodeFromJsonElement<Project>(element)
-    println(data)
+    val element = Json.parseToJsonElement("""
+        {
+            "name": "kotlinx.serialization",
+            "forks": [{"votes": 42}, {"votes": 9000}, {}]
+        }
+    """)
+    val sum = element
+        .jsonObject["forks"]!!
+        .jsonArray.sumOf { it.jsonObject["votes"]?.jsonPrimitive?.int ?: 0 }
+    println(sum)
 }
diff --git a/guide/example/example-json-19.kt b/guide/example/example-json-19.kt
index b001c55..66ce99b 100644
--- a/guide/example/example-json-19.kt
+++ b/guide/example/example-json-19.kt
@@ -4,20 +4,20 @@
 import kotlinx.serialization.*
 import kotlinx.serialization.json.*
 
-import java.math.BigDecimal
-
-val format = Json { prettyPrint = true }
-
 fun main() {
-    val pi = BigDecimal("3.141592653589793238462643383279")
-    
-    val piJsonDouble = JsonPrimitive(pi.toDouble())
-    val piJsonString = JsonPrimitive(pi.toString())
-  
-    val piObject = buildJsonObject {
-        put("pi_double", piJsonDouble)
-        put("pi_string", piJsonString)
+    val element = buildJsonObject {
+        put("name", "kotlinx.serialization")
+        putJsonObject("owner") {
+            put("name", "kotlin")
+        }
+        putJsonArray("forks") {
+            addJsonObject {
+                put("votes", 42)
+            }
+            addJsonObject {
+                put("votes", 9000)
+            }
+        }
     }
-
-    println(format.encodeToString(piObject))
+    println(element)
 }
diff --git a/guide/example/example-json-20.kt b/guide/example/example-json-20.kt
index f522b3f..8f1c786 100644
--- a/guide/example/example-json-20.kt
+++ b/guide/example/example-json-20.kt
@@ -4,24 +4,14 @@
 import kotlinx.serialization.*
 import kotlinx.serialization.json.*
 
-import java.math.BigDecimal
-
-val format = Json { prettyPrint = true }
+@Serializable
+data class Project(val name: String, val language: String)
 
 fun main() {
-    val pi = BigDecimal("3.141592653589793238462643383279")
-
-    // use JsonUnquotedLiteral to encode raw JSON content
-    val piJsonLiteral = JsonUnquotedLiteral(pi.toString())
-
-    val piJsonDouble = JsonPrimitive(pi.toDouble())
-    val piJsonString = JsonPrimitive(pi.toString())
-  
-    val piObject = buildJsonObject {
-        put("pi_literal", piJsonLiteral)
-        put("pi_double", piJsonDouble)
-        put("pi_string", piJsonString)
+    val element = buildJsonObject {
+        put("name", "kotlinx.serialization")
+        put("language", "Kotlin")
     }
-
-    println(format.encodeToString(piObject))
+    val data = Json.decodeFromJsonElement<Project>(element)
+    println(data)
 }
diff --git a/guide/example/example-json-21.kt b/guide/example/example-json-21.kt
index efd6071..2b1d110 100644
--- a/guide/example/example-json-21.kt
+++ b/guide/example/example-json-21.kt
@@ -6,18 +6,18 @@
 
 import java.math.BigDecimal
 
+val format = Json { prettyPrint = true }
+
 fun main() {
-    val piObjectJson = """
-          {
-              "pi_literal": 3.141592653589793238462643383279
-          }
-      """.trimIndent()
+    val pi = BigDecimal("3.141592653589793238462643383279")
     
-    val piObject: JsonObject = Json.decodeFromString(piObjectJson)
-    
-    val piJsonLiteral = piObject["pi_literal"]!!.jsonPrimitive.content
-    
-    val pi = BigDecimal(piJsonLiteral)
-    
-    println(pi)
+    val piJsonDouble = JsonPrimitive(pi.toDouble())
+    val piJsonString = JsonPrimitive(pi.toString())
+  
+    val piObject = buildJsonObject {
+        put("pi_double", piJsonDouble)
+        put("pi_string", piJsonString)
+    }
+
+    println(format.encodeToString(piObject))
 }
diff --git a/guide/example/example-json-22.kt b/guide/example/example-json-22.kt
index e64ab06..f334ce5 100644
--- a/guide/example/example-json-22.kt
+++ b/guide/example/example-json-22.kt
@@ -4,7 +4,25 @@
 import kotlinx.serialization.*
 import kotlinx.serialization.json.*
 
+import java.math.BigDecimal
+
+val format = Json { prettyPrint = true }
+
 fun main() {
-    // caution: creating null with JsonUnquotedLiteral will cause an exception! 
-    JsonUnquotedLiteral("null")
+    val pi = BigDecimal("3.141592653589793238462643383279")
+
+    // use JsonUnquotedLiteral to encode raw JSON content
+    @OptIn(ExperimentalSerializationApi::class)
+    val piJsonLiteral = JsonUnquotedLiteral(pi.toString())
+
+    val piJsonDouble = JsonPrimitive(pi.toDouble())
+    val piJsonString = JsonPrimitive(pi.toString())
+  
+    val piObject = buildJsonObject {
+        put("pi_literal", piJsonLiteral)
+        put("pi_double", piJsonDouble)
+        put("pi_string", piJsonString)
+    }
+
+    println(format.encodeToString(piObject))
 }
diff --git a/guide/example/example-json-23.kt b/guide/example/example-json-23.kt
index ffa9f7d..14f70e2 100644
--- a/guide/example/example-json-23.kt
+++ b/guide/example/example-json-23.kt
@@ -4,29 +4,20 @@
 import kotlinx.serialization.*
 import kotlinx.serialization.json.*
 
-import kotlinx.serialization.builtins.*
-
-@Serializable
-data class Project(
-    val name: String,
-    @Serializable(with = UserListSerializer::class)
-    val users: List<User>
-)
-
-@Serializable
-data class User(val name: String)
-
-object UserListSerializer : JsonTransformingSerializer<List<User>>(ListSerializer(User.serializer())) {
-    // If response is not an array, then it is a single object that should be wrapped into the array
-    override fun transformDeserialize(element: JsonElement): JsonElement =
-        if (element !is JsonArray) JsonArray(listOf(element)) else element
-}
+import java.math.BigDecimal
 
 fun main() {
-    println(Json.decodeFromString<Project>("""
-        {"name":"kotlinx.serialization","users":{"name":"kotlin"}}
-    """))
-    println(Json.decodeFromString<Project>("""
-        {"name":"kotlinx.serialization","users":[{"name":"kotlin"},{"name":"jetbrains"}]}
-    """))
+    val piObjectJson = """
+          {
+              "pi_literal": 3.141592653589793238462643383279
+          }
+      """.trimIndent()
+    
+    val piObject: JsonObject = Json.decodeFromString(piObjectJson)
+    
+    val piJsonLiteral = piObject["pi_literal"]!!.jsonPrimitive.content
+    
+    val pi = BigDecimal(piJsonLiteral)
+    
+    println(pi)
 }
diff --git a/guide/example/example-json-24.kt b/guide/example/example-json-24.kt
index 010bd27..3452c6c 100644
--- a/guide/example/example-json-24.kt
+++ b/guide/example/example-json-24.kt
@@ -4,27 +4,8 @@
 import kotlinx.serialization.*
 import kotlinx.serialization.json.*
 
-import kotlinx.serialization.builtins.*
-
-@Serializable
-data class Project(
-    val name: String,
-    @Serializable(with = UserListSerializer::class)
-    val users: List<User>
-)
-
-@Serializable
-data class User(val name: String)
-
-object UserListSerializer : JsonTransformingSerializer<List<User>>(ListSerializer(User.serializer())) {
-
-    override fun transformSerialize(element: JsonElement): JsonElement {
-        require(element is JsonArray) // this serializer is used only with lists
-        return element.singleOrNull() ?: element
-    }
-}
-
+@OptIn(ExperimentalSerializationApi::class)
 fun main() {
-    val data = Project("kotlinx.serialization", listOf(User("kotlin")))
-    println(Json.encodeToString(data))
+    // caution: creating null with JsonUnquotedLiteral will cause an exception! 
+    JsonUnquotedLiteral("null")
 }
diff --git a/guide/example/example-json-25.kt b/guide/example/example-json-25.kt
index a7d19a7..67c3bf5 100644
--- a/guide/example/example-json-25.kt
+++ b/guide/example/example-json-25.kt
@@ -4,19 +4,29 @@
 import kotlinx.serialization.*
 import kotlinx.serialization.json.*
 
-@Serializable
-class Project(val name: String, val language: String)
+import kotlinx.serialization.builtins.*
 
-object ProjectSerializer : JsonTransformingSerializer<Project>(Project.serializer()) {
-    override fun transformSerialize(element: JsonElement): JsonElement =
-        // Filter out top-level key value pair with the key "language" and the value "Kotlin"
-        JsonObject(element.jsonObject.filterNot {
-            (k, v) -> k == "language" && v.jsonPrimitive.content == "Kotlin"
-        })
+@Serializable
+data class Project(
+    val name: String,
+    @Serializable(with = UserListSerializer::class)
+    val users: List<User>
+)
+
+@Serializable
+data class User(val name: String)
+
+object UserListSerializer : JsonTransformingSerializer<List<User>>(ListSerializer(User.serializer())) {
+    // If response is not an array, then it is a single object that should be wrapped into the array
+    override fun transformDeserialize(element: JsonElement): JsonElement =
+        if (element !is JsonArray) JsonArray(listOf(element)) else element
 }
 
 fun main() {
-    val data = Project("kotlinx.serialization", "Kotlin")
-    println(Json.encodeToString(data)) // using plugin-generated serializer
-    println(Json.encodeToString(ProjectSerializer, data)) // using custom serializer
+    println(Json.decodeFromString<Project>("""
+        {"name":"kotlinx.serialization","users":{"name":"kotlin"}}
+    """))
+    println(Json.decodeFromString<Project>("""
+        {"name":"kotlinx.serialization","users":[{"name":"kotlin"},{"name":"jetbrains"}]}
+    """))
 }
diff --git a/guide/example/example-json-26.kt b/guide/example/example-json-26.kt
index b1b9299..812e496 100644
--- a/guide/example/example-json-26.kt
+++ b/guide/example/example-json-26.kt
@@ -7,30 +7,24 @@
 import kotlinx.serialization.builtins.*
 
 @Serializable
-abstract class Project {
-    abstract val name: String
-}
+data class Project(
+    val name: String,
+    @Serializable(with = UserListSerializer::class)
+    val users: List<User>
+)
 
 @Serializable
-data class BasicProject(override val name: String): Project()
+data class User(val name: String)
 
+object UserListSerializer : JsonTransformingSerializer<List<User>>(ListSerializer(User.serializer())) {
 
-@Serializable
-data class OwnedProject(override val name: String, val owner: String) : Project()
-
-object ProjectSerializer : JsonContentPolymorphicSerializer<Project>(Project::class) {
-    override fun selectDeserializer(element: JsonElement) = when {
-        "owner" in element.jsonObject -> OwnedProject.serializer()
-        else -> BasicProject.serializer()
+    override fun transformSerialize(element: JsonElement): JsonElement {
+        require(element is JsonArray) // this serializer is used only with lists
+        return element.singleOrNull() ?: element
     }
 }
 
 fun main() {
-    val data = listOf(
-        OwnedProject("kotlinx.serialization", "kotlin"),
-        BasicProject("example")
-    )
-    val string = Json.encodeToString(ListSerializer(ProjectSerializer), data)
-    println(string)
-    println(Json.decodeFromString(ListSerializer(ProjectSerializer), string))
+    val data = Project("kotlinx.serialization", listOf(User("kotlin")))
+    println(Json.encodeToString(data))
 }
diff --git a/guide/example/example-json-27.kt b/guide/example/example-json-27.kt
index 5905733..e28b50a 100644
--- a/guide/example/example-json-27.kt
+++ b/guide/example/example-json-27.kt
@@ -4,56 +4,19 @@
 import kotlinx.serialization.*
 import kotlinx.serialization.json.*
 
-import kotlinx.serialization.descriptors.*
-import kotlinx.serialization.encoding.*
-
-@Serializable(with = ResponseSerializer::class)
-sealed class Response<out T> {
-    data class Ok<out T>(val data: T) : Response<T>()
-    data class Error(val message: String) : Response<Nothing>()
-}
-
-class ResponseSerializer<T>(private val dataSerializer: KSerializer<T>) : KSerializer<Response<T>> {
-    override val descriptor: SerialDescriptor = buildSerialDescriptor("Response", PolymorphicKind.SEALED) {
-        element("Ok", dataSerializer.descriptor)
-        element("Error", buildClassSerialDescriptor("Error") {
-          element<String>("message")
-        })
-    }
-
-    override fun deserialize(decoder: Decoder): Response<T> {
-        // Decoder -> JsonDecoder
-        require(decoder is JsonDecoder) // this class can be decoded only by Json
-        // JsonDecoder -> JsonElement
-        val element = decoder.decodeJsonElement()
-        // JsonElement -> value
-        if (element is JsonObject && "error" in element)
-            return Response.Error(element["error"]!!.jsonPrimitive.content)
-        return Response.Ok(decoder.json.decodeFromJsonElement(dataSerializer, element))
-    }
-
-    override fun serialize(encoder: Encoder, value: Response<T>) {
-        // Encoder -> JsonEncoder
-        require(encoder is JsonEncoder) // This class can be encoded only by Json
-        // value -> JsonElement
-        val element = when (value) {
-            is Response.Ok -> encoder.json.encodeToJsonElement(dataSerializer, value.data)
-            is Response.Error -> buildJsonObject { put("error", value.message) }
-        }
-        // JsonElement -> JsonEncoder
-        encoder.encodeJsonElement(element)
-    }
-}
-
 @Serializable
-data class Project(val name: String)
+class Project(val name: String, val language: String)
+
+object ProjectSerializer : JsonTransformingSerializer<Project>(Project.serializer()) {
+    override fun transformSerialize(element: JsonElement): JsonElement =
+        // Filter out top-level key value pair with the key "language" and the value "Kotlin"
+        JsonObject(element.jsonObject.filterNot {
+            (k, v) -> k == "language" && v.jsonPrimitive.content == "Kotlin"
+        })
+}
 
 fun main() {
-    val responses = listOf(
-        Response.Ok(Project("kotlinx.serialization")),
-        Response.Error("Not found")
-    )
-    val string = Json.encodeToString(responses)
-    println(string)
-    println(Json.decodeFromString<List<Response<Project>>>(string))
+    val data = Project("kotlinx.serialization", "Kotlin")
+    println(Json.encodeToString(data)) // using plugin-generated serializer
+    println(Json.encodeToString(ProjectSerializer, data)) // using custom serializer
 }
diff --git a/guide/example/example-json-28.kt b/guide/example/example-json-28.kt
index a3fab61..52ca872 100644
--- a/guide/example/example-json-28.kt
+++ b/guide/example/example-json-28.kt
@@ -4,34 +4,33 @@
 import kotlinx.serialization.*
 import kotlinx.serialization.json.*
 
-import kotlinx.serialization.descriptors.*
-import kotlinx.serialization.encoding.*
+import kotlinx.serialization.builtins.*
 
-data class UnknownProject(val name: String, val details: JsonObject)
+@Serializable
+abstract class Project {
+    abstract val name: String
+}
 
-object UnknownProjectSerializer : KSerializer<UnknownProject> {
-    override val descriptor: SerialDescriptor = buildClassSerialDescriptor("UnknownProject") {
-        element<String>("name")
-        element<JsonElement>("details")
-    }
+@Serializable
+data class BasicProject(override val name: String): Project()
 
-    override fun deserialize(decoder: Decoder): UnknownProject {
-        // Cast to JSON-specific interface
-        val jsonInput = decoder as? JsonDecoder ?: error("Can be deserialized only by JSON")
-        // Read the whole content as JSON
-        val json = jsonInput.decodeJsonElement().jsonObject
-        // Extract and remove name property
-        val name = json.getValue("name").jsonPrimitive.content
-        val details = json.toMutableMap()
-        details.remove("name")
-        return UnknownProject(name, JsonObject(details))
-    }
 
-    override fun serialize(encoder: Encoder, value: UnknownProject) {
-        error("Serialization is not supported")
+@Serializable
+data class OwnedProject(override val name: String, val owner: String) : Project()
+
+object ProjectSerializer : JsonContentPolymorphicSerializer<Project>(Project::class) {
+    override fun selectDeserializer(element: JsonElement) = when {
+        "owner" in element.jsonObject -> OwnedProject.serializer()
+        else -> BasicProject.serializer()
     }
 }
 
 fun main() {
-    println(Json.decodeFromString(UnknownProjectSerializer, """{"type":"unknown","name":"example","maintainer":"Unknown","license":"Apache 2.0"}"""))
+    val data = listOf(
+        OwnedProject("kotlinx.serialization", "kotlin"),
+        BasicProject("example")
+    )
+    val string = Json.encodeToString(ListSerializer(ProjectSerializer), data)
+    println(string)
+    println(Json.decodeFromString(ListSerializer(ProjectSerializer), string))
 }
diff --git a/guide/example/example-json-29.kt b/guide/example/example-json-29.kt
new file mode 100644
index 0000000..41245ff
--- /dev/null
+++ b/guide/example/example-json-29.kt
@@ -0,0 +1,34 @@
+// This file was automatically generated from json.md by Knit tool. Do not edit.
+package example.exampleJson29
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+@Serializable
+sealed class Project {
+    abstract val name: String
+}
+
+@OptIn(ExperimentalSerializationApi::class)
+@KeepGeneratedSerializer
+@Serializable(with = BasicProjectSerializer::class)
+@SerialName("basic")
+data class BasicProject(override val name: String): Project()
+
+object BasicProjectSerializer : JsonTransformingSerializer<BasicProject>(BasicProject.generatedSerializer()) {
+    override fun transformDeserialize(element: JsonElement): JsonElement {
+        val jsonObject = element.jsonObject
+        return if ("basic-name" in jsonObject) {
+            val nameElement = jsonObject["basic-name"] ?: throw IllegalStateException()
+            JsonObject(mapOf("name" to nameElement))
+        } else {
+            jsonObject
+        }
+    }
+}
+
+
+fun main() {
+    val project = Json.decodeFromString<Project>("""{"type":"basic","basic-name":"example"}""")
+    println(project)
+}
diff --git a/guide/example/example-json-30.kt b/guide/example/example-json-30.kt
new file mode 100644
index 0000000..fe379df
--- /dev/null
+++ b/guide/example/example-json-30.kt
@@ -0,0 +1,59 @@
+// This file was automatically generated from json.md by Knit tool. Do not edit.
+package example.exampleJson30
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+
+@Serializable(with = ResponseSerializer::class)
+sealed class Response<out T> {
+    data class Ok<out T>(val data: T) : Response<T>()
+    data class Error(val message: String) : Response<Nothing>()
+}
+
+class ResponseSerializer<T>(private val dataSerializer: KSerializer<T>) : KSerializer<Response<T>> {
+    override val descriptor: SerialDescriptor = buildClassSerialDescriptor("Response") {
+        element("Ok", dataSerializer.descriptor)
+        element("Error", buildClassSerialDescriptor("Error") {
+          element<String>("message")
+        })
+    }
+
+    override fun deserialize(decoder: Decoder): Response<T> {
+        // Decoder -> JsonDecoder
+        require(decoder is JsonDecoder) // this class can be decoded only by Json
+        // JsonDecoder -> JsonElement
+        val element = decoder.decodeJsonElement()
+        // JsonElement -> value
+        if (element is JsonObject && "error" in element)
+            return Response.Error(element["error"]!!.jsonPrimitive.content)
+        return Response.Ok(decoder.json.decodeFromJsonElement(dataSerializer, element))
+    }
+
+    override fun serialize(encoder: Encoder, value: Response<T>) {
+        // Encoder -> JsonEncoder
+        require(encoder is JsonEncoder) // This class can be encoded only by Json
+        // value -> JsonElement
+        val element = when (value) {
+            is Response.Ok -> encoder.json.encodeToJsonElement(dataSerializer, value.data)
+            is Response.Error -> buildJsonObject { put("error", value.message) }
+        }
+        // JsonElement -> JsonEncoder
+        encoder.encodeJsonElement(element)
+    }
+}
+
+@Serializable
+data class Project(val name: String)
+
+fun main() {
+    val responses = listOf(
+        Response.Ok(Project("kotlinx.serialization")),
+        Response.Error("Not found")
+    )
+    val string = Json.encodeToString(responses)
+    println(string)
+    println(Json.decodeFromString<List<Response<Project>>>(string))
+}
diff --git a/guide/example/example-json-31.kt b/guide/example/example-json-31.kt
new file mode 100644
index 0000000..faaa0ff
--- /dev/null
+++ b/guide/example/example-json-31.kt
@@ -0,0 +1,37 @@
+// This file was automatically generated from json.md by Knit tool. Do not edit.
+package example.exampleJson31
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+
+import kotlinx.serialization.descriptors.*
+import kotlinx.serialization.encoding.*
+
+data class UnknownProject(val name: String, val details: JsonObject)
+
+object UnknownProjectSerializer : KSerializer<UnknownProject> {
+    override val descriptor: SerialDescriptor = buildClassSerialDescriptor("UnknownProject") {
+        element<String>("name")
+        element<JsonElement>("details")
+    }
+
+    override fun deserialize(decoder: Decoder): UnknownProject {
+        // Cast to JSON-specific interface
+        val jsonInput = decoder as? JsonDecoder ?: error("Can be deserialized only by JSON")
+        // Read the whole content as JSON
+        val json = jsonInput.decodeJsonElement().jsonObject
+        // Extract and remove name property
+        val name = json.getValue("name").jsonPrimitive.content
+        val details = json.toMutableMap()
+        details.remove("name")
+        return UnknownProject(name, JsonObject(details))
+    }
+
+    override fun serialize(encoder: Encoder, value: UnknownProject) {
+        error("Serialization is not supported")
+    }
+}
+
+fun main() {
+    println(Json.decodeFromString(UnknownProjectSerializer, """{"type":"unknown","name":"example","maintainer":"Unknown","license":"Apache 2.0"}"""))
+}
diff --git a/guide/example/example-serializer-10.kt b/guide/example/example-serializer-10.kt
index 2b75652..a69dece 100644
--- a/guide/example/example-serializer-10.kt
+++ b/guide/example/example-serializer-10.kt
@@ -10,6 +10,7 @@
 
 class ColorIntArraySerializer : KSerializer<Color> {
     private val delegateSerializer = IntArraySerializer()
+    @OptIn(ExperimentalSerializationApi::class)
     override val descriptor = SerialDescriptor("Color", delegateSerializer.descriptor)
 
     override fun serialize(encoder: Encoder, value: Color) {
diff --git a/guide/example/example-serializer-13.kt b/guide/example/example-serializer-13.kt
index f2b0888..8de0c8e 100644
--- a/guide/example/example-serializer-13.kt
+++ b/guide/example/example-serializer-13.kt
@@ -26,7 +26,8 @@
         decoder.decodeStructure(descriptor) {
             var r = -1
             var g = -1
-            var b = -1     
+            var b = -1
+            @OptIn(ExperimentalSerializationApi::class)
             if (decodeSequentially()) { // sequential decoding protocol
                 r = decodeIntElement(descriptor, 0)           
                 g = decodeIntElement(descriptor, 1)  
diff --git a/guide/example/example-serializer-18.kt b/guide/example/example-serializer-18.kt
index 9987e82..b1ce1c9 100644
--- a/guide/example/example-serializer-18.kt
+++ b/guide/example/example-serializer-18.kt
@@ -7,6 +7,7 @@
 import kotlinx.serialization.descriptors.*
 
 import java.util.Date
+import java.util.TimeZone
 import java.text.SimpleDateFormat
   
 object DateAsLongSerializer : KSerializer<Date> {
@@ -17,7 +18,11 @@
 
 object DateAsSimpleTextSerializer: KSerializer<Date> {
     override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("DateAsSimpleText", PrimitiveKind.LONG)
-    private val format = SimpleDateFormat("yyyy-MM-dd")
+    private val format = SimpleDateFormat("yyyy-MM-dd").apply {
+        // Here we explicitly set time zone to UTC so output for this sample remains locale-independent.
+        // Depending on your needs, you may have to adjust or remove this line.
+        setTimeZone(TimeZone.getTimeZone("UTC"))
+    }
     override fun serialize(encoder: Encoder, value: Date) = encoder.encodeString(format.format(value))
     override fun deserialize(decoder: Decoder): Date = format.parse(decoder.decodeString())
 }
diff --git a/guide/example/example-serializer-20.kt b/guide/example/example-serializer-20.kt
index 38b72e7..812c05b 100644
--- a/guide/example/example-serializer-20.kt
+++ b/guide/example/example-serializer-20.kt
@@ -6,17 +6,28 @@
 import kotlinx.serialization.encoding.*
 import kotlinx.serialization.descriptors.*
 
-import java.util.Date
-import java.text.SimpleDateFormat
+object ColorAsStringSerializer : KSerializer<Color> {
+    override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Color", PrimitiveKind.STRING)
 
-@Serializable          
-class ProgrammingLanguage(
-    val name: String,
-    @Contextual 
-    val stableReleaseDate: Date
-)
+    override fun serialize(encoder: Encoder, value: Color) {
+        val string = value.rgb.toString(16).padStart(6, '0')
+        encoder.encodeString(string)
+    }
+
+    override fun deserialize(decoder: Decoder): Color {
+        val string = decoder.decodeString()
+        return Color(string.toInt(16))
+    }
+}
+
+@OptIn(ExperimentalSerializationApi::class)
+@KeepGeneratedSerializer
+@Serializable(with = ColorAsStringSerializer::class)
+class Color(val rgb: Int)
+
 
 fun main() {
-    val data = ProgrammingLanguage("Kotlin", SimpleDateFormat("yyyy-MM-ddX").parse("2016-02-15+00"))
-    println(Json.encodeToString(data))
-}
+    val green = Color(0x00ff00)
+    println(Json.encodeToString(green))
+    println(Json.encodeToString(Color.generatedSerializer(), green))
+}  
diff --git a/guide/example/example-serializer-21.kt b/guide/example/example-serializer-21.kt
index 9a24b0a..c2ae6c0 100644
--- a/guide/example/example-serializer-21.kt
+++ b/guide/example/example-serializer-21.kt
@@ -6,15 +6,8 @@
 import kotlinx.serialization.encoding.*
 import kotlinx.serialization.descriptors.*
 
-import kotlinx.serialization.modules.*
 import java.util.Date
 import java.text.SimpleDateFormat
-  
-object DateAsLongSerializer : KSerializer<Date> {
-    override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Date", PrimitiveKind.LONG)
-    override fun serialize(encoder: Encoder, value: Date) = encoder.encodeLong(value.time)
-    override fun deserialize(decoder: Decoder): Date = Date(decoder.decodeLong())
-}
 
 @Serializable          
 class ProgrammingLanguage(
@@ -23,13 +16,7 @@
     val stableReleaseDate: Date
 )
 
-private val module = SerializersModule { 
-    contextual(DateAsLongSerializer)
-}
-
-val format = Json { serializersModule = module }
-
 fun main() {
     val data = ProgrammingLanguage("Kotlin", SimpleDateFormat("yyyy-MM-ddX").parse("2016-02-15+00"))
-    println(format.encodeToString(data))
+    println(Json.encodeToString(data))
 }
diff --git a/guide/example/example-serializer-22.kt b/guide/example/example-serializer-22.kt
index 4eba74b..c236030 100644
--- a/guide/example/example-serializer-22.kt
+++ b/guide/example/example-serializer-22.kt
@@ -6,13 +6,30 @@
 import kotlinx.serialization.encoding.*
 import kotlinx.serialization.descriptors.*
 
-// NOT @Serializable
-class Project(val name: String, val language: String)
-                           
-@Serializer(forClass = Project::class)
-object ProjectSerializer
+import kotlinx.serialization.modules.*
+import java.util.Date
+import java.text.SimpleDateFormat
+  
+object DateAsLongSerializer : KSerializer<Date> {
+    override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Date", PrimitiveKind.LONG)
+    override fun serialize(encoder: Encoder, value: Date) = encoder.encodeLong(value.time)
+    override fun deserialize(decoder: Decoder): Date = Date(decoder.decodeLong())
+}
+
+@Serializable          
+class ProgrammingLanguage(
+    val name: String,
+    @Contextual 
+    val stableReleaseDate: Date
+)
+
+private val module = SerializersModule { 
+    contextual(DateAsLongSerializer)
+}
+
+val format = Json { serializersModule = module }
 
 fun main() {
-    val data = Project("kotlinx.serialization", "Kotlin")
-    println(Json.encodeToString(ProjectSerializer, data))    
+    val data = ProgrammingLanguage("Kotlin", SimpleDateFormat("yyyy-MM-ddX").parse("2016-02-15+00"))
+    println(format.encodeToString(data))
 }
diff --git a/guide/example/example-serializer-23.kt b/guide/example/example-serializer-23.kt
index 4b7de25..78d537f 100644
--- a/guide/example/example-serializer-23.kt
+++ b/guide/example/example-serializer-23.kt
@@ -6,23 +6,14 @@
 import kotlinx.serialization.encoding.*
 import kotlinx.serialization.descriptors.*
 
-// NOT @Serializable, will use external serializer
-class Project(
-    // val in a primary constructor -- serialized
-    val name: String
-) {
-    var stars: Int = 0 // property with getter & setter -- serialized
- 
-    val path: String // getter only -- not serialized
-        get() = "kotlin/$name"                                         
+// NOT @Serializable
+class Project(val name: String, val language: String)
 
-    private var locked: Boolean = false // private, not accessible -- not serialized 
-}              
-
+@OptIn(ExperimentalSerializationApi::class)
 @Serializer(forClass = Project::class)
 object ProjectSerializer
 
 fun main() {
-    val data = Project("kotlinx.serialization").apply { stars = 9000 }
-    println(Json.encodeToString(ProjectSerializer, data))
+    val data = Project("kotlinx.serialization", "Kotlin")
+    println(Json.encodeToString(ProjectSerializer, data))    
 }
diff --git a/guide/example/example-serializer-24.kt b/guide/example/example-serializer-24.kt
new file mode 100644
index 0000000..52b5be7
--- /dev/null
+++ b/guide/example/example-serializer-24.kt
@@ -0,0 +1,29 @@
+// This file was automatically generated from serializers.md by Knit tool. Do not edit.
+package example.exampleSerializer24
+
+import kotlinx.serialization.*
+import kotlinx.serialization.json.*
+import kotlinx.serialization.encoding.*
+import kotlinx.serialization.descriptors.*
+
+// NOT @Serializable, will use external serializer
+class Project(
+    // val in a primary constructor -- serialized
+    val name: String
+) {
+    var stars: Int = 0 // property with getter & setter -- serialized
+ 
+    val path: String // getter only -- not serialized
+        get() = "kotlin/$name"                                         
+
+    private var locked: Boolean = false // private, not accessible -- not serialized 
+}
+
+@OptIn(ExperimentalSerializationApi::class)
+@Serializer(forClass = Project::class)
+object ProjectSerializer
+
+fun main() {
+    val data = Project("kotlinx.serialization").apply { stars = 9000 }
+    println(Json.encodeToString(ProjectSerializer, data))
+}
diff --git a/guide/test/FormatsTest.kt b/guide/test/FormatsTest.kt
index c20a9f2..f305a59 100644
--- a/guide/test/FormatsTest.kt
+++ b/guide/test/FormatsTest.kt
@@ -62,10 +62,20 @@
     @Test
     fun testExampleFormats08() {
         captureOutput("ExampleFormats08") { example.exampleFormats08.main() }.verifyOutputLines(
+            "0a03546f6d1203313233",
+            "0a054a657272791a03373839",
+            "Data(name=Tom, phone=HomePhone(number=123))",
+            "Data(name=Jerry, phone=WorkPhone(number=789))"
+        )
+    }
+
+    @Test
+    fun testExampleFormats09() {
+        captureOutput("ExampleFormats09") { example.exampleFormats09.main() }.verifyOutputLines(
             "syntax = \"proto2\";",
             "",
             "",
-            "// serial name 'example.exampleFormats08.SampleData'",
+            "// serial name 'example.exampleFormats09.SampleData'",
             "message SampleData {",
             "  required int64 amount = 1;",
             "  optional string description = 2;",
@@ -77,25 +87,17 @@
     }
 
     @Test
-    fun testExampleFormats09() {
-        captureOutput("ExampleFormats09") { example.exampleFormats09.main() }.verifyOutputLines(
+    fun testExampleFormats10() {
+        captureOutput("ExampleFormats10") { example.exampleFormats10.main() }.verifyOutputLines(
             "name = kotlinx.serialization",
             "owner.name = kotlin"
         )
     }
 
     @Test
-    fun testExampleFormats10() {
-        captureOutput("ExampleFormats10") { example.exampleFormats10.main() }.verifyOutputLines(
-            "[kotlinx.serialization, kotlin, 9000]"
-        )
-    }
-
-    @Test
     fun testExampleFormats11() {
         captureOutput("ExampleFormats11") { example.exampleFormats11.main() }.verifyOutputLines(
-            "[kotlinx.serialization, kotlin, 9000]",
-            "Project(name=kotlinx.serialization, owner=User(name=kotlin), votes=9000)"
+            "[kotlinx.serialization, kotlin, 9000]"
         )
     }
 
@@ -110,30 +112,38 @@
     @Test
     fun testExampleFormats13() {
         captureOutput("ExampleFormats13") { example.exampleFormats13.main() }.verifyOutputLines(
-            "[kotlinx.serialization, 2, kotlin, jetbrains, 9000]",
-            "Project(name=kotlinx.serialization, owners=[User(name=kotlin), User(name=jetbrains)], votes=9000)"
+            "[kotlinx.serialization, kotlin, 9000]",
+            "Project(name=kotlinx.serialization, owner=User(name=kotlin), votes=9000)"
         )
     }
 
     @Test
     fun testExampleFormats14() {
         captureOutput("ExampleFormats14") { example.exampleFormats14.main() }.verifyOutputLines(
-            "[kotlinx.serialization, !!, kotlin, NULL]",
-            "Project(name=kotlinx.serialization, owner=User(name=kotlin), votes=null)"
+            "[kotlinx.serialization, 2, kotlin, jetbrains, 9000]",
+            "Project(name=kotlinx.serialization, owners=[User(name=kotlin), User(name=jetbrains)], votes=9000)"
         )
     }
 
     @Test
     fun testExampleFormats15() {
         captureOutput("ExampleFormats15") { example.exampleFormats15.main() }.verifyOutputLines(
-            "{00}{15}kotlinx.serialization{00}{06}Kotlin",
-            "Project(name=kotlinx.serialization, language=Kotlin)"
+            "[kotlinx.serialization, !!, kotlin, NULL]",
+            "Project(name=kotlinx.serialization, owner=User(name=kotlin), votes=null)"
         )
     }
 
     @Test
     fun testExampleFormats16() {
         captureOutput("ExampleFormats16") { example.exampleFormats16.main() }.verifyOutputLines(
+            "{00}{15}kotlinx.serialization{00}{06}Kotlin",
+            "Project(name=kotlinx.serialization, language=Kotlin)"
+        )
+    }
+
+    @Test
+    fun testExampleFormats17() {
+        captureOutput("ExampleFormats17") { example.exampleFormats17.main() }.verifyOutputLines(
             "{00}{15}kotlinx.serialization{04}{0A}{0B}{0C}{0D}",
             "Project(name=kotlinx.serialization, attachment=[10, 11, 12, 13])"
         )
diff --git a/guide/test/JsonTest.kt b/guide/test/JsonTest.kt
index 0c5ed85..35de209 100644
--- a/guide/test/JsonTest.kt
+++ b/guide/test/JsonTest.kt
@@ -40,105 +40,120 @@
     @Test
     fun testExampleJson05() {
         captureOutput("ExampleJson05") { example.exampleJson05.main() }.verifyOutputLines(
-            "Project(name=kotlinx.serialization, language=Kotlin)"
+            "{\"name\":\"kotlinx.serialization\",\"language\":\"Kotlin\",\"website\":null}"
         )
     }
 
     @Test
     fun testExampleJson06() {
         captureOutput("ExampleJson06") { example.exampleJson06.main() }.verifyOutputLines(
-            "{\"name\":\"kotlinx.serialization\",\"language\":\"Kotlin\",\"website\":null}"
-        )
-    }
-
-    @Test
-    fun testExampleJson07() {
-        captureOutput("ExampleJson07") { example.exampleJson07.main() }.verifyOutputLines(
             "{\"name\":\"kotlinx.serialization\",\"language\":\"Kotlin\"}",
             "Project(name=kotlinx.serialization, language=Kotlin, version=1.2.2, website=null, description=null)"
         )
     }
 
     @Test
+    fun testExampleJson07() {
+        captureOutput("ExampleJson07") { example.exampleJson07.main() }.verifyOutputLines(
+            "Project(name=kotlinx.serialization, language=Kotlin)"
+        )
+    }
+
+    @Test
     fun testExampleJson08() {
         captureOutput("ExampleJson08") { example.exampleJson08.main() }.verifyOutputLines(
-            "[{\"name\":\"kotlinx.serialization\"},\"Serialization\",{\"name\":\"kotlinx.coroutines\"},\"Coroutines\"]"
+            "Brush(foreground=BLACK, background=null)"
         )
     }
 
     @Test
     fun testExampleJson09() {
         captureOutput("ExampleJson09") { example.exampleJson09.main() }.verifyOutputLines(
-            "{\"value\":NaN}"
+            "[{\"name\":\"kotlinx.serialization\"},\"Serialization\",{\"name\":\"kotlinx.coroutines\"},\"Coroutines\"]"
         )
     }
 
     @Test
     fun testExampleJson10() {
         captureOutput("ExampleJson10") { example.exampleJson10.main() }.verifyOutputLines(
-            "{\"#class\":\"owned\",\"name\":\"kotlinx.coroutines\",\"owner\":\"kotlin\"}"
+            "{\"value\":NaN}"
         )
     }
 
     @Test
     fun testExampleJson11() {
         captureOutput("ExampleJson11") { example.exampleJson11.main() }.verifyOutputLines(
-            "{\"message\":{\"message_type\":\"my.app.BaseMessage\",\"message\":\"not found\"},\"error\":{\"message_type\":\"my.app.GenericError\",\"error_code\":404}}"
+            "{\"#class\":\"owned\",\"name\":\"kotlinx.coroutines\",\"owner\":\"kotlin\"}"
         )
     }
 
     @Test
     fun testExampleJson12() {
         captureOutput("ExampleJson12") { example.exampleJson12.main() }.verifyOutputLines(
-            "{\"name\":\"kotlinx.coroutines\",\"owner\":\"kotlin\"}"
+            "{\"message\":{\"message_type\":\"my.app.BaseMessage\",\"message\":\"not found\"},\"error\":{\"message_type\":\"my.app.GenericError\",\"error_code\":404}}"
         )
     }
 
     @Test
     fun testExampleJson13() {
         captureOutput("ExampleJson13") { example.exampleJson13.main() }.verifyOutputLines(
-            "CasesList(cases=[VALUE_A, VALUE_B])"
+            "{\"name\":\"kotlinx.coroutines\",\"owner\":\"kotlin\"}"
         )
     }
 
     @Test
     fun testExampleJson14() {
         captureOutput("ExampleJson14") { example.exampleJson14.main() }.verifyOutputLines(
-            "{\"project_name\":\"kotlinx.serialization\",\"project_owner\":\"Kotlin\"}"
+            "CasesList(cases=[VALUE_A, VALUE_B])"
         )
     }
 
     @Test
     fun testExampleJson15() {
         captureOutput("ExampleJson15") { example.exampleJson15.main() }.verifyOutputLines(
-            "{\"name\":\"kotlinx.serialization\",\"language\":\"Kotlin\"}"
+            "{\"project_name\":\"kotlinx.serialization\",\"project_owner\":\"Kotlin\"}"
         )
     }
 
     @Test
     fun testExampleJson16() {
         captureOutput("ExampleJson16") { example.exampleJson16.main() }.verifyOutputLines(
-            "9042"
+            "{\"base64Input\":\"Zm9vIHN0cmluZw==\"}",
+            "foo string"
         )
     }
 
     @Test
     fun testExampleJson17() {
         captureOutput("ExampleJson17") { example.exampleJson17.main() }.verifyOutputLines(
-            "{\"name\":\"kotlinx.serialization\",\"owner\":{\"name\":\"kotlin\"},\"forks\":[{\"votes\":42},{\"votes\":9000}]}"
+            "{\"name\":\"kotlinx.serialization\",\"language\":\"Kotlin\"}"
         )
     }
 
     @Test
     fun testExampleJson18() {
         captureOutput("ExampleJson18") { example.exampleJson18.main() }.verifyOutputLines(
-            "Project(name=kotlinx.serialization, language=Kotlin)"
+            "9042"
         )
     }
 
     @Test
     fun testExampleJson19() {
         captureOutput("ExampleJson19") { example.exampleJson19.main() }.verifyOutputLines(
+            "{\"name\":\"kotlinx.serialization\",\"owner\":{\"name\":\"kotlin\"},\"forks\":[{\"votes\":42},{\"votes\":9000}]}"
+        )
+    }
+
+    @Test
+    fun testExampleJson20() {
+        captureOutput("ExampleJson20") { example.exampleJson20.main() }.verifyOutputLines(
+            "Project(name=kotlinx.serialization, language=Kotlin)"
+        )
+    }
+
+    @Test
+    fun testExampleJson21() {
+        captureOutput("ExampleJson21") { example.exampleJson21.main() }.verifyOutputLines(
             "{",
             "    \"pi_double\": 3.141592653589793,",
             "    \"pi_string\": \"3.141592653589793238462643383279\"",
@@ -147,8 +162,8 @@
     }
 
     @Test
-    fun testExampleJson20() {
-        captureOutput("ExampleJson20") { example.exampleJson20.main() }.verifyOutputLines(
+    fun testExampleJson22() {
+        captureOutput("ExampleJson22") { example.exampleJson22.main() }.verifyOutputLines(
             "{",
             "    \"pi_literal\": 3.141592653589793238462643383279,",
             "    \"pi_double\": 3.141592653589793,",
@@ -158,61 +173,68 @@
     }
 
     @Test
-    fun testExampleJson21() {
-        captureOutput("ExampleJson21") { example.exampleJson21.main() }.verifyOutputLines(
+    fun testExampleJson23() {
+        captureOutput("ExampleJson23") { example.exampleJson23.main() }.verifyOutputLines(
             "3.141592653589793238462643383279"
         )
     }
 
     @Test
-    fun testExampleJson22() {
-        captureOutput("ExampleJson22") { example.exampleJson22.main() }.verifyOutputLinesStart(
-            "Exception in thread \"main\" kotlinx.serialization.json.internal.JsonEncodingException: Creating a literal unquoted value of 'null' is forbidden. If you want to create JSON null literal, use JsonNull object, otherwise, use JsonPrimitive"
-        )
-    }
-
-    @Test
-    fun testExampleJson23() {
-        captureOutput("ExampleJson23") { example.exampleJson23.main() }.verifyOutputLines(
-            "Project(name=kotlinx.serialization, users=[User(name=kotlin)])",
-            "Project(name=kotlinx.serialization, users=[User(name=kotlin), User(name=jetbrains)])"
-        )
-    }
-
-    @Test
     fun testExampleJson24() {
-        captureOutput("ExampleJson24") { example.exampleJson24.main() }.verifyOutputLines(
-            "{\"name\":\"kotlinx.serialization\",\"users\":{\"name\":\"kotlin\"}}"
+        captureOutput("ExampleJson24") { example.exampleJson24.main() }.verifyOutputLinesStart(
+            "Exception in thread \"main\" kotlinx.serialization.json.internal.JsonEncodingException: Creating a literal unquoted value of 'null' is forbidden. If you want to create JSON null literal, use JsonNull object, otherwise, use JsonPrimitive"
         )
     }
 
     @Test
     fun testExampleJson25() {
         captureOutput("ExampleJson25") { example.exampleJson25.main() }.verifyOutputLines(
-            "{\"name\":\"kotlinx.serialization\",\"language\":\"Kotlin\"}",
-            "{\"name\":\"kotlinx.serialization\"}"
+            "Project(name=kotlinx.serialization, users=[User(name=kotlin)])",
+            "Project(name=kotlinx.serialization, users=[User(name=kotlin), User(name=jetbrains)])"
         )
     }
 
     @Test
     fun testExampleJson26() {
         captureOutput("ExampleJson26") { example.exampleJson26.main() }.verifyOutputLines(
-            "[{\"name\":\"kotlinx.serialization\",\"owner\":\"kotlin\"},{\"name\":\"example\"}]",
-            "[OwnedProject(name=kotlinx.serialization, owner=kotlin), BasicProject(name=example)]"
+            "{\"name\":\"kotlinx.serialization\",\"users\":{\"name\":\"kotlin\"}}"
         )
     }
 
     @Test
     fun testExampleJson27() {
         captureOutput("ExampleJson27") { example.exampleJson27.main() }.verifyOutputLines(
-            "[{\"name\":\"kotlinx.serialization\"},{\"error\":\"Not found\"}]",
-            "[Ok(data=Project(name=kotlinx.serialization)), Error(message=Not found)]"
+            "{\"name\":\"kotlinx.serialization\",\"language\":\"Kotlin\"}",
+            "{\"name\":\"kotlinx.serialization\"}"
         )
     }
 
     @Test
     fun testExampleJson28() {
         captureOutput("ExampleJson28") { example.exampleJson28.main() }.verifyOutputLines(
+            "[{\"name\":\"kotlinx.serialization\",\"owner\":\"kotlin\"},{\"name\":\"example\"}]",
+            "[OwnedProject(name=kotlinx.serialization, owner=kotlin), BasicProject(name=example)]"
+        )
+    }
+
+    @Test
+    fun testExampleJson29() {
+        captureOutput("ExampleJson29") { example.exampleJson29.main() }.verifyOutputLines(
+            "BasicProject(name=example)"
+        )
+    }
+
+    @Test
+    fun testExampleJson30() {
+        captureOutput("ExampleJson30") { example.exampleJson30.main() }.verifyOutputLines(
+            "[{\"name\":\"kotlinx.serialization\"},{\"error\":\"Not found\"}]",
+            "[Ok(data=Project(name=kotlinx.serialization)), Error(message=Not found)]"
+        )
+    }
+
+    @Test
+    fun testExampleJson31() {
+        captureOutput("ExampleJson31") { example.exampleJson31.main() }.verifyOutputLines(
             "UnknownProject(name=example, details={\"type\":\"unknown\",\"maintainer\":\"Unknown\",\"license\":\"Apache 2.0\"})"
         )
     }
diff --git a/guide/test/SerializersTest.kt b/guide/test/SerializersTest.kt
index bda3f7f..cb7521d 100644
--- a/guide/test/SerializersTest.kt
+++ b/guide/test/SerializersTest.kt
@@ -141,29 +141,37 @@
 
     @Test
     fun testExampleSerializer20() {
-        captureOutput("ExampleSerializer20") { example.exampleSerializer20.main() }.verifyOutputLinesStart(
+        captureOutput("ExampleSerializer20") { example.exampleSerializer20.main() }.verifyOutputLines(
+            "\"00ff00\"",
+            "{\"rgb\":65280}"
+        )
+    }
+
+    @Test
+    fun testExampleSerializer21() {
+        captureOutput("ExampleSerializer21") { example.exampleSerializer21.main() }.verifyOutputLinesStart(
             "Exception in thread \"main\" kotlinx.serialization.SerializationException: Serializer for class 'Date' is not found.",
             "Please ensure that class is marked as '@Serializable' and that the serialization compiler plugin is applied."
         )
     }
 
     @Test
-    fun testExampleSerializer21() {
-        captureOutput("ExampleSerializer21") { example.exampleSerializer21.main() }.verifyOutputLines(
-            "{\"name\":\"Kotlin\",\"stableReleaseDate\":1455494400000}"
-        )
-    }
-
-    @Test
     fun testExampleSerializer22() {
         captureOutput("ExampleSerializer22") { example.exampleSerializer22.main() }.verifyOutputLines(
-            "{\"name\":\"kotlinx.serialization\",\"language\":\"Kotlin\"}"
+            "{\"name\":\"Kotlin\",\"stableReleaseDate\":1455494400000}"
         )
     }
 
     @Test
     fun testExampleSerializer23() {
         captureOutput("ExampleSerializer23") { example.exampleSerializer23.main() }.verifyOutputLines(
+            "{\"name\":\"kotlinx.serialization\",\"language\":\"Kotlin\"}"
+        )
+    }
+
+    @Test
+    fun testExampleSerializer24() {
+        captureOutput("ExampleSerializer24") { example.exampleSerializer24.main() }.verifyOutputLines(
             "{\"name\":\"kotlinx.serialization\",\"stars\":9000}"
         )
     }
diff --git a/integration-test/build.gradle b/integration-test/build.gradle
deleted file mode 100644
index 6c4e700..0000000
--- a/integration-test/build.gradle
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-buildscript {
-    ext.serialization_version = mainLibVersion
-
-    repositories {
-        mavenCentral()
-        maven { url "https://cache-redirector.jetbrains.com/maven.pkg.jetbrains.space/kotlin/p/kotlin/dev" }
-        mavenLocal() {
-            mavenContent {
-                snapshotsOnly()
-            }
-        }
-    }
-}
-
-// Versions substituted in settings.gradle
-plugins {
-    id 'org.jetbrains.kotlin.multiplatform' version '0'
-    id 'org.jetbrains.kotlin.plugin.serialization' version '0'
-    id 'org.jetbrains.kotlin.kapt' version '0'
-}
-
-repositories {
-    mavenCentral()
-    maven { url "https://cache-redirector.jetbrains.com/maven.pkg.jetbrains.space/kotlin/p/kotlin/dev" }
-    mavenLocal() {
-        mavenContent {
-            snapshotsOnly()
-        }
-    }
-}
-
-group 'com.example'
-version '0.0.1'
-
-apply plugin: 'maven-publish'
-
-kotlin {
-    // Switching module kind for JS is required to run tests
-    js {
-        nodejs {}
-        configure([compilations.main, compilations.test]) {
-            kotlinOptions {
-                sourceMap = true
-                moduleKind = "umd"
-            }
-        }
-    }
-    wasmJs {
-        nodejs()
-    }
-    wasmWasi {
-        nodejs()
-    }
-    jvm {
-        withJava()
-    }
-    macosX64()
-    macosArm64()
-    linuxX64()
-    mingwX64()
-
-    sourceSets {
-        all {
-            languageSettings {
-                optIn('kotlinx.serialization.ExperimentalSerializationApi')
-            }
-        }
-
-        commonMain {
-            dependencies {
-                implementation kotlin('stdlib')
-                implementation "org.jetbrains.kotlinx:kotlinx-serialization-core:$serialization_version"
-                implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:$serialization_version"
-                implementation "org.jetbrains.kotlinx:kotlinx-serialization-protobuf:$serialization_version"
-                implementation "org.jetbrains.kotlinx:kotlinx-serialization-cbor:$serialization_version"
-            }
-        }
-        commonTest {
-            dependencies {
-                implementation kotlin('test-common')
-                implementation kotlin('test-annotations-common')
-            }
-        }
-        jvmMain {
-            dependencies {
-                implementation kotlin('stdlib-jdk8')
-                implementation 'com.google.dagger:dagger:2.13'
-            }
-        }
-        jvmTest {
-            dependencies {
-                implementation kotlin('test')
-                implementation kotlin('test-junit')
-            }
-        }
-        jsMain {
-            dependencies {
-                implementation kotlin('stdlib-js')
-
-            }
-        }
-        jsTest {
-            dependencies {
-                implementation kotlin('test-js')
-            }
-        }
-        wasmJsMain {
-            dependencies {
-                api 'org.jetbrains.kotlin:kotlin-stdlib-wasm-js'
-            }
-        }
-        wasmJsTest {
-            dependencies {
-                api 'org.jetbrains.kotlin:kotlin-test-wasm-js'
-            }
-        }
-        wasmWasiMain {
-            dependencies {
-                api 'org.jetbrains.kotlin:kotlin-stdlib-wasm-wasi'
-            }
-        }
-        wasmWasiTest {
-            dependencies {
-                api 'org.jetbrains.kotlin:kotlin-test-wasm-wasi'
-            }
-        }
-    }
-
-    targets.all {
-        compilations.all {
-            kotlinOptions {
-                freeCompilerArgs += "-Xexpect-actual-classes"
-            }
-        }
-        compilations.main {
-            kotlinOptions {
-                allWarningsAsErrors = true
-            }
-        }
-    }
-}
-
-dependencies {
-    kapt 'com.google.dagger:dagger-compiler:2.13'
-}
-
-task run dependsOn "check"
-
-rootProject.extensions.findByType(org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension.class).with {
-    // canary nodejs that supports recent Wasm GC changes
-    it.nodeVersion = "21.0.0-v8-canary202309167e82ab1fa2"
-    it.nodeDownloadBaseUrl = "https://nodejs.org/download/v8-canary"
-}
-
-tasks.withType(org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinNpmInstallTask).configureEach {
-    args.add("--ignore-engines")
-}
diff --git a/integration-test/build.gradle.kts b/integration-test/build.gradle.kts
new file mode 100644
index 0000000..38da9e3
--- /dev/null
+++ b/integration-test/build.gradle.kts
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+import org.jetbrains.kotlin.gradle.targets.js.nodejs.NodeJsRootExtension
+import org.jetbrains.kotlin.gradle.targets.js.npm.tasks.KotlinNpmInstallTask
+import org.jetbrains.kotlin.gradle.plugin.mpp.*
+
+val serialization_version = property("mainLibVersion") as String
+
+// Versions substituted in settings.gradle.kts
+plugins {
+    id("org.jetbrains.kotlin.multiplatform") version "0"
+    id("org.jetbrains.kotlin.plugin.serialization") version "0"
+    id("org.jetbrains.kotlin.kapt") version "0"
+
+    id("maven-publish")
+}
+
+repositories {
+    mavenCentral()
+    maven("https://cache-redirector.jetbrains.com/maven.pkg.jetbrains.space/kotlin/p/kotlin/dev")
+    mavenLocal {
+        mavenContent {
+            snapshotsOnly()
+        }
+    }
+}
+
+group = "com.example"
+version = "0.0.1"
+
+kotlin {
+    // Switching module kind for JS is required to run tests
+    js {
+        nodejs {}
+        compilations.matching { it.name == "main" || it.name == "test" }.configureEach {
+            kotlinOptions {
+                sourceMap = true
+                moduleKind = "umd"
+            }
+        }
+    }
+    wasmJs {
+        nodejs()
+    }
+    wasmWasi {
+        nodejs()
+    }
+    jvm {
+        withJava()
+    }
+    macosX64()
+    macosArm64()
+    linuxX64()
+    mingwX64()
+
+    sourceSets {
+        all {
+            languageSettings {
+                optIn("kotlinx.serialization.ExperimentalSerializationApi")
+            }
+        }
+
+        commonMain {
+            dependencies {
+                implementation(kotlin("stdlib"))
+                implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:$serialization_version")
+                implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$serialization_version")
+                // To check that all expected artifacts are resolvable:
+                implementation("org.jetbrains.kotlinx:kotlinx-serialization-json-io:$serialization_version")
+                implementation("org.jetbrains.kotlinx:kotlinx-serialization-json-okio:$serialization_version")
+                implementation("org.jetbrains.kotlinx:kotlinx-serialization-protobuf:$serialization_version")
+                implementation("org.jetbrains.kotlinx:kotlinx-serialization-cbor:$serialization_version")
+            }
+        }
+        commonTest {
+            dependencies {
+                implementation(kotlin("test-common"))
+                implementation(kotlin("test-annotations-common"))
+            }
+        }
+        jvmMain {
+            dependencies {
+                implementation(kotlin("stdlib-jdk8"))
+                implementation("com.google.dagger:dagger:2.13")
+                implementation("org.jetbrains.kotlinx:kotlinx-serialization-hocon:$serialization_version")
+            }
+        }
+        jvmTest {
+            dependencies {
+                implementation(kotlin("test"))
+                implementation(kotlin("test-junit"))
+            }
+        }
+        jsMain {
+            dependencies {
+                implementation(kotlin("stdlib-js"))
+
+            }
+        }
+        jsTest {
+            dependencies {
+                implementation(kotlin("test-js"))
+            }
+        }
+        named("wasmJsMain") {
+            dependencies {
+                api("org.jetbrains.kotlin:kotlin-stdlib-wasm-js")
+            }
+        }
+        named("wasmJsTest") {
+            dependencies {
+                api("org.jetbrains.kotlin:kotlin-test-wasm-js")
+            }
+        }
+        named("wasmWasiMain") {
+            dependencies {
+                api("org.jetbrains.kotlin:kotlin-stdlib-wasm-wasi")
+            }
+        }
+        named("wasmWasiTest") {
+            dependencies {
+                api("org.jetbrains.kotlin:kotlin-test-wasm-wasi")
+            }
+        }
+    }
+
+    targets.all {
+        compilations.all {
+            kotlinOptions {
+                freeCompilerArgs += "-Xexpect-actual-classes"
+            }
+        }
+        compilations["main"].kotlinOptions {
+            allWarningsAsErrors = true
+            // Suppress 'K2 kapt is an experimental feature' warning:
+            freeCompilerArgs += "-Xsuppress-version-warnings"
+        }
+    }
+
+    // setup tests running in RELEASE mode
+    targets.withType<KotlinNativeTarget>().configureEach {
+        binaries.test(listOf(NativeBuildType.RELEASE))
+    }
+    targets.withType<KotlinNativeTargetWithTests<*>>().configureEach {
+        testRuns.create("releaseTest") {
+            setExecutionSourceFrom(binaries.getTest(NativeBuildType.RELEASE))
+        }
+    }
+}
+
+dependencies {
+    "kapt"("com.google.dagger:dagger-compiler:2.13")
+}
+
+tasks.withType<KotlinNpmInstallTask>().configureEach {
+    args.add("--ignore-engines")
+}
diff --git a/integration-test/gradle.properties b/integration-test/gradle.properties
index d29c5df..bf55d52 100644
--- a/integration-test/gradle.properties
+++ b/integration-test/gradle.properties
@@ -2,8 +2,8 @@
 # Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
 #
 
-mainKotlinVersion=1.9.22
-mainLibVersion=1.6.4-SNAPSHOT
+mainKotlinVersion=2.0.20
+mainLibVersion=1.7.4-SNAPSHOT
 
 kotlin.code.style=official
 kotlin.js.compiler=ir
@@ -15,6 +15,8 @@
 mocha_teamcity_reporter_version = 2.2.2
 source_map_support_version = 0.5.3
 
+kapt.use.k2=true
+
 # Uncommend & insert path to local Native distribution if you want to test with SNAPSHOT compiler
 #kotlin.native.home=
 
diff --git a/integration-test/kotlin-js-store/yarn.lock b/integration-test/kotlin-js-store/yarn.lock
index 08c4983..70c45d5 100644
--- a/integration-test/kotlin-js-store/yarn.lock
+++ b/integration-test/kotlin-js-store/yarn.lock
@@ -2,10 +2,10 @@
 # yarn lockfile v1
 
 
[email protected]:
-  version "4.1.1"
-  resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
-  integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==
+ansi-colors@^4.1.3:
+  version "4.1.3"
+  resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b"
+  integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==
 
 ansi-regex@^5.0.1:
   version "5.0.1"
@@ -42,14 +42,6 @@
   resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
   integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
 
-brace-expansion@^1.1.7:
-  version "1.1.11"
-  resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
-  integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
-  dependencies:
-    balanced-match "^1.0.0"
-    concat-map "0.0.1"
-
 brace-expansion@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
@@ -64,7 +56,7 @@
   dependencies:
     fill-range "^7.0.1"
 
[email protected]:
+browser-stdout@^1.3.1:
   version "1.3.1"
   resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60"
   integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==
@@ -87,10 +79,10 @@
     ansi-styles "^4.1.0"
     supports-color "^7.1.0"
 
[email protected]:
-  version "3.5.3"
-  resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
-  integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
+chokidar@^3.5.3:
+  version "3.6.0"
+  resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b"
+  integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==
   dependencies:
     anymatch "~3.1.2"
     braces "~3.0.2"
@@ -123,15 +115,10 @@
   resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
   integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
 
[email protected]:
-  version "0.0.1"
-  resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
-  integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
-
[email protected]:
-  version "4.3.4"
-  resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
-  integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
+debug@^4.3.5:
+  version "4.3.6"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b"
+  integrity sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==
   dependencies:
     ms "2.1.2"
 
@@ -140,10 +127,10 @@
   resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837"
   integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==
 
[email protected]:
-  version "5.0.0"
-  resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b"
-  integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==
+diff@^5.2.0:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/diff/-/diff-5.2.0.tgz#26ded047cd1179b78b9537d5ef725503ce1ae531"
+  integrity sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==
 
 emoji-regex@^8.0.0:
   version "8.0.0"
@@ -155,7 +142,7 @@
   resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
   integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
 
[email protected]:
+escape-string-regexp@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
   integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
@@ -167,7 +154,7 @@
   dependencies:
     to-regex-range "^5.0.1"
 
[email protected]:
+find-up@^5.0.0:
   version "5.0.0"
   resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
   integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==
@@ -207,24 +194,23 @@
   dependencies:
     is-glob "^4.0.1"
 
[email protected]:
-  version "7.2.0"
-  resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
-  integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
+glob@^8.1.0:
+  version "8.1.0"
+  resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e"
+  integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==
   dependencies:
     fs.realpath "^1.0.0"
     inflight "^1.0.4"
     inherits "2"
-    minimatch "^3.0.4"
+    minimatch "^5.0.1"
     once "^1.3.0"
-    path-is-absolute "^1.0.0"
 
 has-flag@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
   integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
 
[email protected]:
+he@^1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
   integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
@@ -281,7 +267,7 @@
   resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7"
   integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==
 
[email protected]:
+js-yaml@^4.1.0:
   version "4.1.0"
   resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
   integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
@@ -295,7 +281,7 @@
   dependencies:
     p-locate "^5.0.0"
 
[email protected]:
+log-symbols@^4.1.0:
   version "4.1.0"
   resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503"
   integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==
@@ -303,62 +289,49 @@
     chalk "^4.1.0"
     is-unicode-supported "^0.1.0"
 
[email protected]:
-  version "5.0.1"
-  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b"
-  integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==
+minimatch@^5.0.1, minimatch@^5.1.6:
+  version "5.1.6"
+  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96"
+  integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==
   dependencies:
     brace-expansion "^2.0.1"
 
-minimatch@^3.0.4:
-  version "3.1.2"
-  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
-  integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
[email protected]:
+  version "10.7.0"
+  resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.7.0.tgz#9e5cbed8fa9b37537a25bd1f7fb4f6fc45458b9a"
+  integrity sha512-v8/rBWr2VO5YkspYINnvu81inSz2y3ODJrhO175/Exzor1RcEZZkizgE2A+w/CAXXoESS8Kys5E62dOHGHzULA==
   dependencies:
-    brace-expansion "^1.1.7"
-
[email protected]:
-  version "10.2.0"
-  resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.2.0.tgz#1fd4a7c32ba5ac372e03a17eef435bd00e5c68b8"
-  integrity sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==
-  dependencies:
-    ansi-colors "4.1.1"
-    browser-stdout "1.3.1"
-    chokidar "3.5.3"
-    debug "4.3.4"
-    diff "5.0.0"
-    escape-string-regexp "4.0.0"
-    find-up "5.0.0"
-    glob "7.2.0"
-    he "1.2.0"
-    js-yaml "4.1.0"
-    log-symbols "4.1.0"
-    minimatch "5.0.1"
-    ms "2.1.3"
-    nanoid "3.3.3"
-    serialize-javascript "6.0.0"
-    strip-json-comments "3.1.1"
-    supports-color "8.1.1"
-    workerpool "6.2.1"
-    yargs "16.2.0"
-    yargs-parser "20.2.4"
-    yargs-unparser "2.0.0"
+    ansi-colors "^4.1.3"
+    browser-stdout "^1.3.1"
+    chokidar "^3.5.3"
+    debug "^4.3.5"
+    diff "^5.2.0"
+    escape-string-regexp "^4.0.0"
+    find-up "^5.0.0"
+    glob "^8.1.0"
+    he "^1.2.0"
+    js-yaml "^4.1.0"
+    log-symbols "^4.1.0"
+    minimatch "^5.1.6"
+    ms "^2.1.3"
+    serialize-javascript "^6.0.2"
+    strip-json-comments "^3.1.1"
+    supports-color "^8.1.1"
+    workerpool "^6.5.1"
+    yargs "^16.2.0"
+    yargs-parser "^20.2.9"
+    yargs-unparser "^2.0.0"
 
 [email protected]:
   version "2.1.2"
   resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
   integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
 
[email protected]:
+ms@^2.1.3:
   version "2.1.3"
   resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
   integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
 
[email protected]:
-  version "3.3.3"
-  resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25"
-  integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==
-
 normalize-path@^3.0.0, normalize-path@~3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
@@ -390,11 +363,6 @@
   resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
   integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
 
-path-is-absolute@^1.0.0:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
-  integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
-
 picomatch@^2.0.4, picomatch@^2.2.1:
   version "2.3.1"
   resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
@@ -424,10 +392,10 @@
   resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
   integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
 
[email protected]:
-  version "6.0.0"
-  resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8"
-  integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==
+serialize-javascript@^6.0.2:
+  version "6.0.2"
+  resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2"
+  integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==
   dependencies:
     randombytes "^2.1.0"
 
@@ -460,18 +428,11 @@
   dependencies:
     ansi-regex "^5.0.1"
 
[email protected]:
+strip-json-comments@^3.1.1:
   version "3.1.1"
   resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
   integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
 
[email protected]:
-  version "8.1.1"
-  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
-  integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
-  dependencies:
-    has-flag "^4.0.0"
-
 supports-color@^7.1.0:
   version "7.2.0"
   resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
@@ -479,6 +440,13 @@
   dependencies:
     has-flag "^4.0.0"
 
+supports-color@^8.1.1:
+  version "8.1.1"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
+  integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
+  dependencies:
+    has-flag "^4.0.0"
+
 to-regex-range@^5.0.1:
   version "5.0.1"
   resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
@@ -486,15 +454,15 @@
   dependencies:
     is-number "^7.0.0"
 
[email protected]:
-  version "5.0.4"
-  resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.4.tgz#b217fd20119bd61a94d4011274e0ab369058da3b"
-  integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==
[email protected]:
+  version "5.5.4"
+  resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.4.tgz#d9852d6c82bad2d2eda4fd74a5762a8f5909e9ba"
+  integrity sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==
 
[email protected]:
-  version "6.2.1"
-  resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343"
-  integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==
+workerpool@^6.5.1:
+  version "6.5.1"
+  resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544"
+  integrity sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==
 
 wrap-ansi@^7.0.0:
   version "7.0.0"
@@ -515,17 +483,12 @@
   resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
   integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
 
[email protected]:
-  version "20.2.4"
-  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54"
-  integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==
-
-yargs-parser@^20.2.2:
+yargs-parser@^20.2.2, yargs-parser@^20.2.9:
   version "20.2.9"
   resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
   integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
 
[email protected]:
+yargs-unparser@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb"
   integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==
@@ -535,7 +498,7 @@
     flat "^5.0.2"
     is-plain-obj "^2.1.0"
 
[email protected]:
+yargs@^16.2.0:
   version "16.2.0"
   resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66"
   integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==
diff --git a/integration-test/settings.gradle b/integration-test/settings.gradle.kts
similarity index 68%
rename from integration-test/settings.gradle
rename to integration-test/settings.gradle.kts
index f8cb2d8..c2cb0c4 100644
--- a/integration-test/settings.gradle
+++ b/integration-test/settings.gradle.kts
@@ -1,5 +1,6 @@
 pluginManagement {
     resolutionStrategy {
+        val mainKotlinVersion: String by settings
         eachPlugin {
             if (requested.id.id == "org.jetbrains.kotlin.multiplatform") {
                 useVersion("$mainKotlinVersion")
@@ -15,10 +16,10 @@
 
     repositories {
         mavenCentral()
-        maven { url 'https://plugins.gradle.org/m2/' }
-        maven { url "https://cache-redirector.jetbrains.com/maven.pkg.jetbrains.space/kotlin/p/kotlin/dev" }
+        maven("https://plugins.gradle.org/m2/")
+        maven("https://cache-redirector.jetbrains.com/maven.pkg.jetbrains.space/kotlin/p/kotlin/dev")
         mavenLocal()
     }
 }
 
-rootProject.name = 'kotlinx-serialization-integration-test'
+rootProject.name = "kotlinx-serialization-integration-test"
diff --git a/kotlin-js-store/yarn.lock b/kotlin-js-store/yarn.lock
index 2ecec42..eb19f38 100644
--- a/kotlin-js-store/yarn.lock
+++ b/kotlin-js-store/yarn.lock
@@ -2,10 +2,10 @@
 # yarn lockfile v1
 
 
[email protected]:
-  version "4.1.1"
-  resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348"
-  integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==
+ansi-colors@^4.1.3:
+  version "4.1.3"
+  resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b"
+  integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==
 
 ansi-regex@^5.0.1:
   version "5.0.1"
@@ -42,14 +42,6 @@
   resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
   integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
 
-brace-expansion@^1.1.7:
-  version "1.1.11"
-  resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
-  integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
-  dependencies:
-    balanced-match "^1.0.0"
-    concat-map "0.0.1"
-
 brace-expansion@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae"
@@ -64,7 +56,7 @@
   dependencies:
     fill-range "^7.0.1"
 
[email protected]:
+browser-stdout@^1.3.1:
   version "1.3.1"
   resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60"
   integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==
@@ -87,10 +79,10 @@
     ansi-styles "^4.1.0"
     supports-color "^7.1.0"
 
[email protected]:
-  version "3.5.3"
-  resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
-  integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
+chokidar@^3.5.3:
+  version "3.6.0"
+  resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b"
+  integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==
   dependencies:
     anymatch "~3.1.2"
     braces "~3.0.2"
@@ -123,15 +115,10 @@
   resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
   integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
 
[email protected]:
-  version "0.0.1"
-  resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
-  integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
-
[email protected]:
-  version "4.3.4"
-  resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
-  integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
+debug@^4.3.5:
+  version "4.3.6"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.6.tgz#2ab2c38fbaffebf8aa95fdfe6d88438c7a13c52b"
+  integrity sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==
   dependencies:
     ms "2.1.2"
 
@@ -140,10 +127,10 @@
   resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837"
   integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==
 
[email protected]:
-  version "5.0.0"
-  resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b"
-  integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==
+diff@^5.2.0:
+  version "5.2.0"
+  resolved "https://registry.yarnpkg.com/diff/-/diff-5.2.0.tgz#26ded047cd1179b78b9537d5ef725503ce1ae531"
+  integrity sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==
 
 emoji-regex@^8.0.0:
   version "8.0.0"
@@ -155,7 +142,7 @@
   resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
   integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
 
[email protected]:
+escape-string-regexp@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
   integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
@@ -167,7 +154,7 @@
   dependencies:
     to-regex-range "^5.0.1"
 
[email protected]:
+find-up@^5.0.0:
   version "5.0.0"
   resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc"
   integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==
@@ -207,24 +194,23 @@
   dependencies:
     is-glob "^4.0.1"
 
[email protected]:
-  version "7.2.0"
-  resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
-  integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
+glob@^8.1.0:
+  version "8.1.0"
+  resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e"
+  integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==
   dependencies:
     fs.realpath "^1.0.0"
     inflight "^1.0.4"
     inherits "2"
-    minimatch "^3.0.4"
+    minimatch "^5.0.1"
     once "^1.3.0"
-    path-is-absolute "^1.0.0"
 
 has-flag@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
   integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
 
[email protected]:
+he@^1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
   integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
@@ -281,7 +267,7 @@
   resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7"
   integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==
 
[email protected]:
+js-yaml@^4.1.0:
   version "4.1.0"
   resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
   integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
@@ -295,7 +281,7 @@
   dependencies:
     p-locate "^5.0.0"
 
[email protected]:
+log-symbols@^4.1.0:
   version "4.1.0"
   resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503"
   integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==
@@ -303,62 +289,49 @@
     chalk "^4.1.0"
     is-unicode-supported "^0.1.0"
 
[email protected]:
-  version "5.0.1"
-  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b"
-  integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==
+minimatch@^5.0.1, minimatch@^5.1.6:
+  version "5.1.6"
+  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96"
+  integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==
   dependencies:
     brace-expansion "^2.0.1"
 
-minimatch@^3.0.4:
-  version "3.0.4"
-  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083"
-  integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==
[email protected]:
+  version "10.7.0"
+  resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.7.0.tgz#9e5cbed8fa9b37537a25bd1f7fb4f6fc45458b9a"
+  integrity sha512-v8/rBWr2VO5YkspYINnvu81inSz2y3ODJrhO175/Exzor1RcEZZkizgE2A+w/CAXXoESS8Kys5E62dOHGHzULA==
   dependencies:
-    brace-expansion "^1.1.7"
-
[email protected]:
-  version "10.2.0"
-  resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.2.0.tgz#1fd4a7c32ba5ac372e03a17eef435bd00e5c68b8"
-  integrity sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==
-  dependencies:
-    ansi-colors "4.1.1"
-    browser-stdout "1.3.1"
-    chokidar "3.5.3"
-    debug "4.3.4"
-    diff "5.0.0"
-    escape-string-regexp "4.0.0"
-    find-up "5.0.0"
-    glob "7.2.0"
-    he "1.2.0"
-    js-yaml "4.1.0"
-    log-symbols "4.1.0"
-    minimatch "5.0.1"
-    ms "2.1.3"
-    nanoid "3.3.3"
-    serialize-javascript "6.0.0"
-    strip-json-comments "3.1.1"
-    supports-color "8.1.1"
-    workerpool "6.2.1"
-    yargs "16.2.0"
-    yargs-parser "20.2.4"
-    yargs-unparser "2.0.0"
+    ansi-colors "^4.1.3"
+    browser-stdout "^1.3.1"
+    chokidar "^3.5.3"
+    debug "^4.3.5"
+    diff "^5.2.0"
+    escape-string-regexp "^4.0.0"
+    find-up "^5.0.0"
+    glob "^8.1.0"
+    he "^1.2.0"
+    js-yaml "^4.1.0"
+    log-symbols "^4.1.0"
+    minimatch "^5.1.6"
+    ms "^2.1.3"
+    serialize-javascript "^6.0.2"
+    strip-json-comments "^3.1.1"
+    supports-color "^8.1.1"
+    workerpool "^6.5.1"
+    yargs "^16.2.0"
+    yargs-parser "^20.2.9"
+    yargs-unparser "^2.0.0"
 
 [email protected]:
   version "2.1.2"
   resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
   integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
 
[email protected]:
+ms@^2.1.3:
   version "2.1.3"
   resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
   integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
 
[email protected]:
-  version "3.3.3"
-  resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25"
-  integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==
-
 normalize-path@^3.0.0, normalize-path@~3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
@@ -390,11 +363,6 @@
   resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3"
   integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
 
-path-is-absolute@^1.0.0:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
-  integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
-
 picomatch@^2.0.4, picomatch@^2.2.1:
   version "2.3.1"
   resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
@@ -424,10 +392,10 @@
   resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
   integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
 
[email protected]:
-  version "6.0.0"
-  resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8"
-  integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==
+serialize-javascript@^6.0.2:
+  version "6.0.2"
+  resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2"
+  integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==
   dependencies:
     randombytes "^2.1.0"
 
@@ -460,18 +428,11 @@
   dependencies:
     ansi-regex "^5.0.1"
 
[email protected]:
+strip-json-comments@^3.1.1:
   version "3.1.1"
   resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
   integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
 
[email protected]:
-  version "8.1.1"
-  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
-  integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
-  dependencies:
-    has-flag "^4.0.0"
-
 supports-color@^7.1.0:
   version "7.2.0"
   resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
@@ -479,6 +440,13 @@
   dependencies:
     has-flag "^4.0.0"
 
+supports-color@^8.1.1:
+  version "8.1.1"
+  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c"
+  integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==
+  dependencies:
+    has-flag "^4.0.0"
+
 to-regex-range@^5.0.1:
   version "5.0.1"
   resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
@@ -486,15 +454,15 @@
   dependencies:
     is-number "^7.0.0"
 
[email protected]:
-  version "5.0.4"
-  resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.4.tgz#b217fd20119bd61a94d4011274e0ab369058da3b"
-  integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw==
[email protected]:
+  version "5.5.4"
+  resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.5.4.tgz#d9852d6c82bad2d2eda4fd74a5762a8f5909e9ba"
+  integrity sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==
 
[email protected]:
-  version "6.2.1"
-  resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343"
-  integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==
+workerpool@^6.5.1:
+  version "6.5.1"
+  resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544"
+  integrity sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==
 
 wrap-ansi@^7.0.0:
   version "7.0.0"
@@ -515,17 +483,12 @@
   resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
   integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
 
[email protected]:
-  version "20.2.4"
-  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54"
-  integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==
-
-yargs-parser@^20.2.2:
+yargs-parser@^20.2.2, yargs-parser@^20.2.9:
   version "20.2.9"
   resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
   integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
 
[email protected]:
+yargs-unparser@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb"
   integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==
@@ -535,7 +498,7 @@
     flat "^5.0.2"
     is-plain-obj "^2.1.0"
 
[email protected]:
+yargs@^16.2.0:
   version "16.2.0"
   resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66"
   integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==
diff --git a/rules/common.pro b/rules/common.pro
index f84d7b9..0fbddd5 100644
--- a/rules/common.pro
+++ b/rules/common.pro
@@ -33,3 +33,9 @@
 # If there is no `java.lang.ClassValue` (for example, in Android), then R8/ProGuard will print a warning.
 # However, since in this case they will not be used, we can disable these warnings
 -dontwarn kotlinx.serialization.internal.ClassValueReferences
+
+# disable optimisation for descriptor field because in some versions of ProGuard, optimization generates incorrect bytecode that causes a verification error
+# see https://github.com/Kotlin/kotlinx.serialization/issues/2719
+-keepclassmembers public class **$$serializer {
+    private ** descriptor;
+}
diff --git a/settings.gradle b/settings.gradle
deleted file mode 100644
index ed5256e..0000000
--- a/settings.gradle
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
- */
-
-plugins {
-    id 'org.gradle.toolchains.foojay-resolver-convention' version '0.5.0'
-}
-
-rootProject.name = 'kotlinx-serialization'
-
-include ':kotlinx-serialization-core'
-project(':kotlinx-serialization-core').projectDir = file('./core')
-
-include ':kotlinx-serialization-bom'
-project(':kotlinx-serialization-bom').projectDir = file('./bom')
-
-include ':kotlinx-serialization-json'
-project(':kotlinx-serialization-json').projectDir = file('./formats/json')
-
-include ':kotlinx-serialization-json-okio'
-project(':kotlinx-serialization-json-okio').projectDir = file('./formats/json-okio')
-
-include ':kotlinx-serialization-json-tests'
-project(':kotlinx-serialization-json-tests').projectDir = file('./formats/json-tests')
-
-include ':kotlinx-serialization-protobuf'
-project(':kotlinx-serialization-protobuf').projectDir = file('./formats/protobuf')
-
-include ':kotlinx-serialization-cbor'
-project(':kotlinx-serialization-cbor').projectDir = file('./formats/cbor')
-
-include ':kotlinx-serialization-hocon'
-project(':kotlinx-serialization-hocon').projectDir = file('./formats/hocon')
-
-include ':kotlinx-serialization-properties'
-project(':kotlinx-serialization-properties').projectDir = file('./formats/properties')
-
-include ':benchmark'
-project(':benchmark').projectDir = file('./benchmark')
-
-include ':guide'
-project(':guide').projectDir = file('./guide')
diff --git a/settings.gradle.kts b/settings.gradle.kts
new file mode 100644
index 0000000..0d7c75a
--- /dev/null
+++ b/settings.gradle.kts
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+pluginManagement {
+    repositories {
+        /**
+         * Overrides for Teamcity 'K2 User Projects' + 'Aggregate build / Kotlinx libraries compilation' configuration:
+         * kotlin_repo_url - local repository with snapshot Kotlin compiler
+         * kotlin_version - kotlin version to use
+         * kotlin_language_version - LV to use
+         */
+        val kotlinRepoUrl: String? = providers.gradleProperty("kotlin_repo_url").orNull
+        if (kotlinRepoUrl?.isNotEmpty() == true) {
+            maven(kotlinRepoUrl)
+        }
+        /*
+        * This property group is used to build kotlinx.serialization against Kotlin compiler snapshot.
+        * When build_snapshot_train is set to true, kotlin_version property is overridden with kotlin_snapshot_version.
+        * DO NOT change the name of these properties without adapting kotlinx.train build chain.
+        */
+        val buildSnapshotTrain: String? = providers.gradleProperty("build_snapshot_train").orNull
+        if (buildSnapshotTrain.equals("true", true)) {
+            maven("https://oss.sonatype.org/content/repositories/snapshots")
+        }
+
+        // kotlin-dev with space redirector
+        maven("https://cache-redirector.jetbrains.com/maven.pkg.jetbrains.space/kotlin/p/kotlin/dev")
+
+        maven("https://maven.pkg.jetbrains.space/kotlin/p/dokka/dev")
+        // For Dokka that depends on kotlinx-html
+        maven("https://maven.pkg.jetbrains.space/public/p/kotlinx-html/maven")
+
+        gradlePluginPortal()
+        mavenCentral()
+        mavenLocal()
+    }
+}
+
+plugins {
+    id("org.gradle.toolchains.foojay-resolver-convention") version "0.5.0"
+}
+
+rootProject.name = "kotlinx-serialization"
+
+include(":kotlinx-serialization-core")
+project(":kotlinx-serialization-core").projectDir = file("./core")
+
+include(":kotlinx-serialization-bom")
+project(":kotlinx-serialization-bom").projectDir = file("./bom")
+
+include(":kotlinx-serialization-json")
+project(":kotlinx-serialization-json").projectDir = file("./formats/json")
+
+include(":kotlinx-serialization-json-okio")
+project(":kotlinx-serialization-json-okio").projectDir = file("./formats/json-okio")
+
+include(":kotlinx-serialization-json-io")
+project(":kotlinx-serialization-json-io").projectDir = file("./formats/json-io")
+
+include(":kotlinx-serialization-json-tests")
+project(":kotlinx-serialization-json-tests").projectDir = file("./formats/json-tests")
+
+include(":kotlinx-serialization-protobuf")
+project(":kotlinx-serialization-protobuf").projectDir = file("./formats/protobuf")
+
+include(":kotlinx-serialization-cbor")
+project(":kotlinx-serialization-cbor").projectDir = file("./formats/cbor")
+
+include(":kotlinx-serialization-hocon")
+project(":kotlinx-serialization-hocon").projectDir = file("./formats/hocon")
+
+include(":kotlinx-serialization-properties")
+project(":kotlinx-serialization-properties").projectDir = file("./formats/properties")
+
+include(":benchmark")
+project(":benchmark").projectDir = file("./benchmark")
+
+include(":guide")
+project(":guide").projectDir = file("./guide")
+
+
+dependencyResolutionManagement {
+    versionCatalogs {
+        create("libs") {
+            overriddenKotlinVersion()?.also { overriddenVersion ->
+                logger.info("Overriding Kotlin version: $overriddenVersion")
+                version("kotlin", overriddenVersion)
+            }
+        }
+    }
+}
+
+fun overriddenKotlinVersion(): String? {
+    val kotlinRepoUrl: String? = providers.gradleProperty("kotlin_repo_url").orNull
+    val repoVersion: String? = providers.gradleProperty("kotlin_version").orNull
+
+    val bootstrap: String? = providers.gradleProperty("bootstrap").orNull
+    val bootstrapVersion: String? = providers.gradleProperty("kotlin.version.snapshot").orNull
+
+    val buildSnapshotTrain: String? = providers.gradleProperty("build_snapshot_train").orNull
+    val trainVersion: String? = providers.gradleProperty("kotlin_snapshot_version").orNull
+
+    if (kotlinRepoUrl?.isNotEmpty() == true) {
+        return repoVersion ?: throw IllegalArgumentException("\"kotlin_version\" Gradle property should be defined")
+    } else if (bootstrap != null) {
+        return bootstrapVersion ?: throw IllegalArgumentException("\"kotlin.version.snapshot\" Gradle property should be defined")
+    }
+    if (buildSnapshotTrain?.isNotEmpty() == true) {
+        return trainVersion ?: throw IllegalArgumentException("\"kotlin_snapshot_version\" should be defined when building with snapshot compiler")
+    }
+    return null
+}