From 49c7a154e1c82a64a581351dd8f78f2590b5875c Mon Sep 17 00:00:00 2001 From: Daniel Csorba Date: Thu, 30 Oct 2025 18:16:49 +0100 Subject: [PATCH 1/7] feat: adding JSON overview and Json configuration docs --- .../topics/configure-json-serialization.md | 66 ++ .../serialization-json-configuration.md | 707 ++++++++++++++++++ 2 files changed, 773 insertions(+) create mode 100644 docs-website/topics/configure-json-serialization.md create mode 100644 docs-website/topics/serialization-json-configuration.md diff --git a/docs-website/topics/configure-json-serialization.md b/docs-website/topics/configure-json-serialization.md new file mode 100644 index 000000000..928a2eee5 --- /dev/null +++ b/docs-website/topics/configure-json-serialization.md @@ -0,0 +1,66 @@ +[//]: # (title: JSON serialization overview) + +The Kotlin serialization library allows you to easily convert Kotlin objects to JSON and back. +The [`Json`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/) class is the primary tool for this, offering flexibility in how JSON is generated and parsed. +You can configure `Json` instances to handle specific JSON behaviors or use it without any changes for basic tasks. + +With the `Json` class, you can: + +* Serialize Kotlin objects to JSON strings using the [`encodeToString()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/encode-to-string.html) function. +* Deserialize JSON strings back to Kotlin objects with the [`decodeFromString()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/decode-from-string.html) function. +* Work directly with the [`JsonElement`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-element/) when handling complex JSON structures using the [`encodeToJsonElement()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/encode-to-json-element.html) and the [`decodeFromJsonElement()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/decode-from-json-element.html) functions. + +Before you start, import the following declarations from the serialization libraries: + +```kotlin +import kotlinx.serialization.* +import kotlinx.serialization.json.* +``` + +Here's a simple example that demonstrates how JSON serialization works in Kotlin: + +```kotlin +// Imports declarations from the serialization and JSON handling libraries +import kotlinx.serialization.* +import kotlinx.serialization.json.* + +//sampleStart +@Serializable +data class User(val name: String, val age: Int) + +fun main() { + // Creates a Json instance with default settings + val json = Json + + // Creates a User object + val user = User("Alice", 30) + + // Converts the User object to a JSON string + val jsonString = json.encodeToString(user) + println(jsonString) + // {"name":"Alice","age":30} + + // Converts the JSON string back to a User object + val deserializedUser = json.decodeFromString(jsonString) + println(deserializedUser) + // User(name=Alice, age=30) +//sampleEnd +} +``` +{kotlin-runnable="true"} + +In addition to using the default configuration, you can [customize the `Json` instance](serialization-json-configuration.md) for specific different use cases, +such as ignoring unknown keys: + +```kotlin +// Configures a Json instance to ignore unknown keys +val customJson = Json { + ignoreUnknownKeys = true +} +``` + +## What's next + +* Learn how to [customize `Json` instances](serialization-json-configuration.md) to address different use cases for serialization and deserialization. +* Explore [advanced JSON element handling](serialization-json-elements.md) to manipulate and work with JSON data before it is parsed or serialized. +* Discover how to [transform JSON during serialization and deserialization](serialization-transform-json.md) for more control over your data. diff --git a/docs-website/topics/serialization-json-configuration.md b/docs-website/topics/serialization-json-configuration.md new file mode 100644 index 000000000..661750051 --- /dev/null +++ b/docs-website/topics/serialization-json-configuration.md @@ -0,0 +1,707 @@ +[//]: # (title: Customize the Json instance) + +The default [`Json`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/) class enforces Kotlin type safety and only allows values that can be serialized into standard JSON. + +You can handle more flexible or non-standard JSON features by creating a custom `Json` instance with the [`Json()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json.html) builder function: + +```kotlin +// Creates a Json instance based on the default configuration, ignoring unknown keys +val customJson = Json { + ignoreUnknownKeys = true +} + +// Use the customJson instance with the same syntax as the default one to encode a string +val jsonString = customJson.encodeToString(user) +println(jsonString) +``` + +`Json` instances created this way are immutable and thread-safe, which makes them safe to store in a top-level property for reuse. + +You can also base a new `Json` instance on an existing one and modify its settings with the [`JsonBuilder`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/) DSL: + +```kotlin +// Creates a new instance based on an existing Json +val lenientJson = Json(customJson) { + isLenient = true + prettyPrint = true +} +``` + +> Reusing custom `Json` instances improves performance by allowing them to cache class-specific information. +> +{style="tip"} + +The following sections cover the various `Json` class configuration features. + +## Customize JSON serialization + +You can control how Kotlin objects are serialized to JSON by configuring a `Json` instance. +This allows you to format the output, choose whether to include or omit certain values, +and customize how specific types are encoded. + +### Pretty printing + +By default, `Json` produces a compact, single-line output. + +You can add indentations and line breaks for better readability by enabling pretty printing in the output. +To do so, set the [`prettyPrint`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/pretty-print.html) property to `true` in a `Json` instance: + +```kotlin +// Imports declarations from the serialization and JSON handling libraries +import kotlinx.serialization.* +import kotlinx.serialization.json.* + +//sampleStart +// Creates a custom Json format +val format = Json { prettyPrint = true } + +@Serializable +data class Project(val name: String, val language: String) + +fun main() { + val data = Project("kotlinx.serialization", "Kotlin") + + // Prints the JSON output with line breaks and indentations + println(format.encodeToString(data)) +} +//sampleEnd +``` +{kotlin-runnable="true"} + +This example prints the following result: + +```text +{ + "name": "kotlinx.serialization", + "language": "Kotlin" +} +``` + +### Encode default values + +By default, the JSON serializer doesn't encode default property values because they are automatically applied to missing properties during decoding. +This behavior is especially useful for nullable properties with null defaults, as it avoids writing unnecessary `null` values. +For more details, see the [Manage serialization of default properties](serialization-customization-options.md#manage-the-serialization-of-default-properties-with-encodeddefault) section. + +To change this default behavior, set the [`encodeDefaults`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/encode-defaults.html) property to `true` in a `Json` instance: + +```kotlin +// Imports declarations from the serialization and JSON handling libraries +import kotlinx.serialization.* +import kotlinx.serialization.json.* + +//sampleStart +// Configures a Json instance to encode default values +val format = Json { encodeDefaults = true } + +@Serializable +class Project( + val name: String, + val language: String = "Kotlin", + val website: String? = null +) + +fun main() { + val data = Project("kotlinx.serialization") + + // Encodes all the property values including the default ones + println(format.encodeToString(data)) + // {"name":"kotlinx.serialization","language":"Kotlin","website":null} +} +//sampleEnd +``` +{kotlin-runnable="true"} + +### Omit explicit nulls + +By default, all `null` values are encoded into JSON output. +To omit `null` values, set the [`explicitNulls`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/explicit-nulls.html) property to `false` in a `Json` instance: + +```kotlin +// Imports declarations from the serialization and JSON handling libraries +import kotlinx.serialization.* +import kotlinx.serialization.json.* + +//sampleStart +// Configures a Json instance to omit null values during serialization +val format = Json { explicitNulls = false } + +@Serializable +data class Project( + val name: String, + val language: String, + val version: String? = "1.2.2", + val website: String?, + val description: String? = null +) + +fun main() { + val data = Project("kotlinx.serialization", "Kotlin", null, null, null) + val json = format.encodeToString(data) + + // Omits version, website, and description properties from the JSON output + println(json) + // {"name":"kotlinx.serialization","language":"Kotlin"} + + // Treats missing nullable properties without defaults as null + // Fills properties that have defaults with their default values + println(format.decodeFromString(json)) + // Project(name=kotlinx.serialization, language=Kotlin, version=1.2.2, website=null, description=null) +} +//sampleEnd +``` +{kotlin-runnable="true"} + +When `explicitNulls` is set to `false` encoding and decoding can become asymmetrical. +In this example, the `version` property is `null` before encoding but decodes to `1.2.2`. + +> You can configure the decoder to handle certain invalid input values by treating them as missing properties with the [`coerceInputValues`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/coerce-input-values.html) property. +> For more information, see the [Coerce input values](#coerce-input-values) section. +> +{style="tip"} + +### Encode structured map keys + +The JSON format doesn't natively support maps with structured keys, because JSON keys are strings that represent only primitives or enums. +To serialize maps with user-defined class keys, use the [`allowStructuredMapKeys`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/allow-structured-map-keys.html) property: + +```kotlin +// Imports declarations from the serialization and JSON handling libraries +import kotlinx.serialization.* +import kotlinx.serialization.json.* + +//sampleStart +// Configures a Json instance to encode maps with structured keys +val format = Json { allowStructuredMapKeys = true } + +@Serializable +data class Project(val name: String) + +fun main() { + val map = mapOf( + Project("kotlinx.serialization") to "Serialization", + Project("kotlinx.coroutines") to "Coroutines" + ) + // Serializes the map with structured keys as a JSON array: + // [key1, value1, key2, value2,...] + println(format.encodeToString(map)) + // [{"name":"kotlinx.serialization"},"Serialization",{"name":"kotlinx.coroutines"},"Coroutines"] +} +//sampleEnd +``` +{kotlin-runnable="true"} + +### Encode special floating-point values + +By default, special floating-point values like [`Double.NaN`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-double/-na-n.html) +and infinities aren't supported in JSON because the JSON specification prohibits them. + +To enable their encoding, set the [`allowSpecialFloatingPointValues`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/allow-special-floating-point-values.html) property to `true` in a `Json` instance: + +```kotlin +// Imports declarations from the serialization and JSON handling libraries +import kotlinx.serialization.* +import kotlinx.serialization.json.* + +//sampleStart +// Configures a Json instance to allow special floating-point values +val format = Json { allowSpecialFloatingPointValues = true } + +@Serializable +class Data( + val value: Double +) + +fun main() { + val data = Data(Double.NaN) + // Produces a non-standard JSON output used for representing special floating-point values + println(format.encodeToString(data)) + // {"value":NaN} +} +//sampleEnd +``` +{kotlin-runnable="true"} + +### Specify class discriminator for polymorphism + +When working with [polymorphic data](serialization-polymorphism.md), you can use the +[`classDiscriminator`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/class-discriminator.html) property to specify a key name that identifies the type of the serialized polymorphic object. +Combined with an [explicit serial name defined with the `@SerialName` annotation](serialization-customization-options.md#customize-serial-names), +this approach gives you full control over the resulting JSON structure: + +```kotlin +// Imports declarations from the serialization and JSON handling libraries +import kotlinx.serialization.* +import kotlinx.serialization.json.* + +//sampleStart +// Configures a Json instance to use a custom class discriminator +val format = Json { classDiscriminator = "#class" } + +@Serializable +sealed class Project { + abstract val name: String +} + +// Specifies a custom serial name for the OwnedProject class +@Serializable +@SerialName("owned") +class OwnedProject(override val name: String, val owner: String) : Project() + +// Specifies a custom serial name for the SimpleProject class +@Serializable +@SerialName("simple") +class SimpleProject(override val name: String) : Project() + +fun main() { + val simpleProject: Project = SimpleProject("kotlinx.serialization") + val ownedProject: Project = OwnedProject("kotlinx.coroutines", "kotlin") + + // Serializes SimpleProject with #class: "simple" + println(format.encodeToString(simpleProject)) + // {"#class":"simple","name":"kotlinx.serialization"} + + // Serializes OwnedProject with #class: "owned" + println(format.encodeToString(ownedProject)) + // {"#class":"owned","name":"kotlinx.coroutines","owner":"kotlin"} +} +//sampleEnd +``` +{kotlin-runnable="true"} + +> [`@JsonClassDiscriminator`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-class-discriminator/) is [Experimental](components-stability.md#stability-levels-explained). To opt in, use the `@OptIn(ExperimentalSerializationApi::class)` annotation or the compiler option `-opt-in=kotlinx.serialization.ExperimentalSerializationApi`. +> +{style="warning"} + +While the `classDiscriminator` property in a `Json` instance lets you specify a single discriminator key for all polymorphic types, the [`@JsonClassDiscriminator`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-class-discriminator/) annotation offers more flexibility. +It allows you to define a custom discriminator directly on the base class, which is automatically inherited by all its subclasses. + +> To learn more about inheritable serial annotations, see [`@InheritableSerialInfo`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-inheritable-serial-info/). +> +{style="tip"} + +Here's an example: + +```kotlin +// Imports declarations from the serialization and JSON handling libraries +import kotlinx.serialization.* +import kotlinx.serialization.json.* + +//sampleStart +// The @JsonClassDiscriminator annotation is inheritable, so all subclasses of Base will have the same discriminator +@Serializable +@OptIn(ExperimentalSerializationApi::class) +@JsonClassDiscriminator("message_type") +sealed class Base + +// Inherits the discriminator from Base +@Serializable +sealed class ErrorClass: Base() + +// Defines a class that combines a message and an optional error +@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" } + +fun main() { + val data = Message(BaseMessage("not found"), GenericError(404)) + // Uses the discriminator from Base for all subclasses + println(format.encodeToString(data)) + // {"message":{"message_type":"my.app.BaseMessage","message":"not found"},"error":{"message_type":"my.app.GenericError","error_code":404}} +} +//sampleEnd +``` +{kotlin-runnable="true"} + +> You can't specify different class discriminators in subclasses of a sealed base class. +> Only hierarchies with distinct, non-overlapping subclasses can define their own discriminators. +> +> When both specify a discriminator, `@JsonClassDiscriminator` takes priority over the one in the `Json` configuration. +> +{style="note"} + +#### Set class discriminator output mode + +Use the [`JsonBuilder.classDiscriminatorMode`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/class-discriminator-mode.html) property to control how class discriminators are added to your JSON output. +By default, the [discriminator is only added for polymorphic types](#specify-class-discriminator-for-polymorphism), which is useful when working with [polymorphic class hierarchies](serialization-polymorphism.md). + +To adjust this behavior, set the [`ClassDiscriminatorMode`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-class-discriminator-mode/) property to one of these options: + +* [`POLYMORPHIC`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-class-discriminator-mode/-p-o-l-y-m-o-r-p-h-i-c/): (Default) Adds the class discriminator only for polymorphic types. +* [`ALL_JSON_OBJECTS`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-class-discriminator-mode/-a-l-l_-j-s-o-n_-o-b-j-e-c-t-s/): Adds the class discriminator to all JSON objects, wherever possible. +* [`NONE`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-class-discriminator-mode/-n-o-n-e/): Omits the class discriminator entirely. + +Here's an example with the `ClassDiscriminatorMode` property set to `NONE`: + +```kotlin +// Imports declarations from the serialization and JSON handling libraries +import kotlinx.serialization.* +import kotlinx.serialization.json.* + +//sampleStart +// Configures a Json instance to omit the class discriminator from the output +val format = Json { classDiscriminatorMode = ClassDiscriminatorMode.NONE } + +@Serializable +sealed class Project { + abstract val name: String +} + +@Serializable +class OwnedProject(override val name: String, val owner: String) : Project() + +fun main() { + val data: Project = OwnedProject("kotlinx.coroutines", "kotlin") + // Serializes without a discriminator + println(format.encodeToString(data)) + // {"name":"kotlinx.coroutines","owner":"kotlin"} +} +//sampleEnd +``` +{kotlin-runnable="true"} + +> Without the discriminator, the Kotlin serialization library can't deserialize this output back into the appropriate type. +> +{style="note"} + +## Customize JSON deserialization + +Kotlin's `Json` parser provides several settings that let you customize how JSON data is parsed and deserialized. + +### Lenient parsing + +By default, the `Json` parser enforces strict JSON rules to ensure compliance with the [RFC-4627](https://www.ietf.org/rfc/rfc4627.txt) specification, +which requires keys and string literals to be quoted. + +To relax these restrictions, set the [`isLenient`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/is-lenient.html) property to `true` in a `Json` instance: + +```kotlin +// Imports declarations from the serialization and JSON handling libraries +import kotlinx.serialization.* +import kotlinx.serialization.json.* + +//sampleStart +val format = Json { isLenient = true } + +enum class Status { SUPPORTED } + +@Serializable +data class Project(val name: String, val status: Status, val votes: Int) + +fun main() { + // Decodes a JSON string with lenient parsing + // Lenient parsing allows unquoted keys, string and enum values, and quoted integers + val data = format.decodeFromString(""" + { + name : kotlinx.serialization, + status : SUPPORTED, + votes : "9000" + } + """) + println(data) + // Project(name=kotlinx.serialization, status=SUPPORTED, votes=9000) +} +//sampleEnd +``` +{kotlin-runnable="true"} + +### Ignore unknown keys + +When working with JSON data from third-party services or other dynamic sources, new properties may be added to the JSON objects over time. + +By default, unknown keys (the property names in the JSON input) result in an error during deserialization. +To prevent this, set the [`ignoreUnknownKeys`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/ignore-unknown-keys.html) property +to `true` in a `Json` instance: + +```kotlin +// Imports declarations from the serialization and JSON handling libraries +import kotlinx.serialization.* +import kotlinx.serialization.json.* + +//sampleStart +// Creates a Json instance to ignore unknown keys +val format = Json { ignoreUnknownKeys = true } + +@Serializable +data class Project(val name: String) + +fun main() { + val data = format.decodeFromString(""" + {"name":"kotlinx.serialization","language":"Kotlin"} + """) + // The language key is ignored because it's not in the Project class + println(data) + // Project(name=kotlinx.serialization) +} +//sampleEnd +``` +{kotlin-runnable="true"} + +#### Ignore unknown keys for specific classes + + +> This feature is [Experimental](components-stability.md#stability-levels-explained). To opt in, use the `@OptIn(ExperimentalSerializationApi::class)` annotation or the compiler option `-opt-in=kotlinx.serialization.ExperimentalSerializationApi`. +> +{style="warning"} + +Instead of [enabling `ignoreUnknownKeys` for all classes](#ignore-unknown-keys), +you can ignore unknown keys only for specific classes by annotating them with the `@JsonIgnoreUnknownKeys` annotation. +This lets you keep strict deserialization by default while allowing leniency only where you need it. + +Here's an example: + +```kotlin +// Imports declarations from the serialization and JSON handling libraries +import kotlinx.serialization.* +import kotlinx.serialization.json.* + +//sampleStart +@OptIn(ExperimentalSerializationApi::class) +@Serializable +// Unknown properties in Outer are ignored during deserialization +@JsonIgnoreUnknownKeys +data class Outer(val a: Int, val inner: Inner) + +@Serializable +data class Inner(val x: String) + +fun main() { + val outer = Json.decodeFromString( + """{"a":1,"inner":{"x":"value"},"unknownKey":42}""" + ) + println(outer) + // Outer(a=1, inner=Inner(x=value)) + + // Throws an exception + // unknownKey inside inner is NOT ignored because Inner is not annotated + println( + Json.decodeFromString( + """{"a":1,"inner":{"x":"value","unknownKey":"unexpected"}}""" + ) + ) +} +//sampleEnd +``` +{kotlin-runnable="true" min-compiler-version="2.2" validate="false"} + +In this example, `Inner` throws a `SerializationException` for unknown keys because it isn't annotated with `@JsonIgnoreUnknownKeys`. + +### Coerce input values + +When working with JSON data from third-party services or other dynamic sources, the format can evolve over time. +This may cause exceptions during decoding when actual values don't match the expected types. + +The default `Json` implementation is [strict about input types](serialization-customization-options.md#the-serializable-annotation). +To relax this restriction, set the [`coerceInputValues`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/coerce-input-values.html) property to `true` in a `Json` instance: + +```kotlin +// Imports declarations from the serialization and JSON handling libraries +import kotlinx.serialization.* +import kotlinx.serialization.json.* + +//sampleStart +val format = Json { coerceInputValues = true } + +@Serializable +data class Project(val name: String, val language: String = "Kotlin") + +fun main() { + val data = format.decodeFromString(""" + {"name":"kotlinx.serialization","language":null} + """) + + // Coerces the invalid null value for language to its default value + println(data) + // Project(name=kotlinx.serialization, language=Kotlin) +} +//sampleEnd +``` +{kotlin-runnable="true"} + +The `coerceInputValues` property only affects decoding. It treats certain invalid input values as if the corresponding property were missing. +Currently, it applies to: + +* `null` inputs for non-nullable types +* unknown values for enums + +> This list may be expanded in future versions, making `Json` instances with this property even more permissive +> by replacing invalid values with defaults or `null`. +> +{style="note"} + +If a value is missing, it's replaced with a default property value if one exists. + +For enums the value is replaced with `null` only if: + +* No default is defined. +* The [`explicitNulls`](#omit-explicit-nulls) property is set to `false`. +* The property is nullable. + +You can combine `coerceInputValues` with the `explicitNulls` property to handle invalid enum values: + +```kotlin +// Imports declarations from the serialization and JSON handling libraries +import kotlinx.serialization.* +import kotlinx.serialization.json.* + +//sampleStart +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() { + + // Coerces the unknown foreground value to its default and background to null + val brush = json.decodeFromString("""{"foreground":"pink", "background":"purple"}""") + println(brush) + // Brush(foreground=BLACK, background=null) +} +//sampleEnd +``` +{kotlin-runnable="true"} + +## Customize name mapping between JSON and Kotlin + +Some JSON data may not perfectly align with Kotlin's naming conventions or expected formats. +To address these challenges, the Kotlin serialization library provides several tools to manage naming discrepancies, +handle multiple JSON property names, and ensure consistent naming strategies across serialized data. + +### Accept alternative JSON property names for a single Kotlin property + +When JSON property names change between schema versions, +you can [use the `@SerialName` annotation to rename a JSON property](serialization-customization-options.md#customize-serial-names). + +However, this prevents decoding data that still uses previous property names. +To accept alternative JSON property names for a single property, use the [`@JsonNames`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-names/) annotation: + +```kotlin +// Imports declarations from the serialization and JSON handling libraries +import kotlinx.serialization.* +import kotlinx.serialization.json.* + +//sampleStart +@Serializable +// Maps both name and title JSON properties to the name property +data class Project(@JsonNames("title") val name: String) + +fun main() { + val project = Json.decodeFromString("""{"name":"kotlinx.serialization"}""") + println(project) + // Project(name=kotlinx.serialization) + + val oldProject = Json.decodeFromString("""{"title":"kotlinx.coroutines"}""") + // Both name and title Json properties correspond to name property + println(oldProject) + // Project(name=kotlinx.coroutines) +} +//sampleEnd +``` +{kotlin-runnable="true"} + +> The `@JsonNames` annotation is enabled by the [`useAlternativeNames`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/use-alternative-names.html) property in [`JsonBuilder`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/). +> This property is set to `true` by default and allows `Json` to recognize and decode multiple names for a single property. +> +> If you aren't using `@JsonNames` and want to improve performance, +> especially when skipping many unknown properties with `ignoreUnknownKeys`, you can set this property to `false`. +> +{style="note"} + +### Decode enums in a case-insensitive manner + +[Kotlin's naming conventions](coding-conventions.md#property-names) recommend naming enum values +in uppercase with underscores or in upper camel case. +By default, `Json` uses the exact names of Kotlin enum constants when decoding. + +However, JSON data from external sources might use lowercase or mixed-case names. +To handle such cases, configure the `Json` instance to decode enum values case-insensitively with the [`JsonBuilder.decodeEnumsCaseInsensitive`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/decode-enums-case-insensitive.html) property: + +```kotlin +// Imports declarations from the serialization and JSON handling libraries +import kotlinx.serialization.* +import kotlinx.serialization.json.* + +//sampleStart +// Configures a Json instance to decode enum values in a case-insensitive way +val format = Json { decodeEnumsCaseInsensitive = true } + +enum class Cases { VALUE_A, @JsonNames("Alternative") VALUE_B } + +@Serializable +data class CasesList(val cases: List) + +fun main() { + // Decodes enum values regardless of their case, including alternative names + println(format.decodeFromString("""{"cases":["value_A", "alternative"]}""")) + // CasesList(cases=[VALUE_A, VALUE_B]) +} +//sampleEnd +``` +{kotlin-runnable="true"} + +> This property applies to both [serial names](serialization-customization-options.md#customize-serial-names) and [alternative names](#accept-alternative-json-property-names-for-a-single-kotlin-property) specified with the `@JsonNames` annotation, +> ensuring that all values are decoded successfully. This property doesn't affect encoding. +> +{style="note"} + +### Apply a global naming strategy + +When property names in JSON input differ from those in Kotlin, you can specify the name of each property explicitly using the [`@SerialName`](serialization-customization-options.md#customize-serial-names) annotation. +However, when migrating from other frameworks or a legacy codebase, you might need to transform every serial name in the same way. + +For these scenarios, you can specify a global naming strategy using the [JsonBuilder.namingStrategy](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/naming-strategy.html) property in a `Json` instance. +The Kotlin serialization library provides built-in strategies, such as [JsonNamingStrategy.SnakeCase](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-naming-strategy/-builtins/-snake-case.html): + +```kotlin +// Imports declarations from the serialization and JSON handling libraries +import kotlinx.serialization.* +import kotlinx.serialization.json.* + +//sampleStart +@Serializable +data class Project(val projectName: String, val projectOwner: String) + +// Configures a Json instance to apply SnakeCase naming strategy +val format = Json { namingStrategy = JsonNamingStrategy.SnakeCase } + +fun main() { + val project = format.decodeFromString("""{"project_name":"kotlinx.coroutines", "project_owner":"Kotlin"}""") + // Serializes and deserializes as if all serial names are transformed from camel case to snake case + println(format.encodeToString(project.copy(projectName = "kotlinx.serialization"))) + // {"project_name":"kotlinx.serialization","project_owner":"Kotlin"} +} +//sampleEnd +``` +{kotlin-runnable="true"} + +When using a global naming strategy with [`JsonNamingStrategy`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-naming-strategy/), keep the following in mind: + +* **The transformation applies to all properties**, whether the serial name is derived from the property name or explicitly defined with the [`@SerialName`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serial-name/) annotation. + You can't exclude a property from transformation by specifying a serial name. + To keep specific names unchanged during serialization, use the `@JsonNames` annotation instead. +* If a transformed name conflicts with other transformed property names or with alternative names specified by the `@JsonNames` annotation, + deserialization fails with an exception. +* Global naming strategies are implicit. + It makes it difficult to determine the serialized names by looking at the class definition. + This can make tasks like **Find Usages**, **Rename** in IDEs, or full-text searches using tools like `grep`, more difficult, potentially increasing the risk of bugs and maintenance costs. + +Given these factors, consider the trade-offs carefully before implementing global naming strategies in your application. + +## What's next + +* Explore [advanced JSON element handling](serialization-json-elements.md) to manipulate and work with JSON data before it's parsed or serialized. +* Discover how to [transform JSON during serialization and deserialization](serialization-transform-json.md) for more control over your data. From 92e0a2aeac20a18619793d91933056f232317658 Mon Sep 17 00:00:00 2001 From: Daniel Csorba Date: Thu, 20 Nov 2025 16:30:46 +0100 Subject: [PATCH 2/7] update: implementing first round of review comments --- .../topics/configure-json-serialization.md | 31 +++- .../serialization-json-configuration.md | 134 +++++++++++++----- 2 files changed, 127 insertions(+), 38 deletions(-) diff --git a/docs-website/topics/configure-json-serialization.md b/docs-website/topics/configure-json-serialization.md index 928a2eee5..ce1a0381e 100644 --- a/docs-website/topics/configure-json-serialization.md +++ b/docs-website/topics/configure-json-serialization.md @@ -2,25 +2,26 @@ The Kotlin serialization library allows you to easily convert Kotlin objects to JSON and back. The [`Json`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/) class is the primary tool for this, offering flexibility in how JSON is generated and parsed. -You can configure `Json` instances to handle specific JSON behaviors or use it without any changes for basic tasks. +You can configure `Json` instances to handle specific JSON behaviors or use its default instance for basic tasks. With the `Json` class, you can: * Serialize Kotlin objects to JSON strings using the [`encodeToString()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/encode-to-string.html) function. * Deserialize JSON strings back to Kotlin objects with the [`decodeFromString()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/decode-from-string.html) function. -* Work directly with the [`JsonElement`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-element/) when handling complex JSON structures using the [`encodeToJsonElement()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/encode-to-json-element.html) and the [`decodeFromJsonElement()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/decode-from-json-element.html) functions. +* [Work directly with the `JsonElement`](serialization-json-elements.md) when handling complex JSON structures using the [`encodeToJsonElement()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/encode-to-json-element.html) and the [`decodeFromJsonElement()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/decode-from-json-element.html) functions. +* Use JVM streams with the [`encodeToStream()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/encode-to-stream.html) [`.decodeFromStream()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/decode-from-stream.html), and [`.decodeToSequence()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/decode-to-sequence.html) functions to work with JSON directly from input and output streams. -Before you start, import the following declarations from the serialization libraries: +Before you start, import the following declarations from the serialization library: ```kotlin import kotlinx.serialization.* import kotlinx.serialization.json.* ``` -Here's a simple example that demonstrates how JSON serialization works in Kotlin: +Here's a simple example that uses the default `Json` instance to show how JSON serialization works in Kotlin: ```kotlin -// Imports declarations from the serialization and JSON handling libraries +// Imports declarations from the serialization library import kotlinx.serialization.* import kotlinx.serialization.json.* @@ -29,7 +30,7 @@ import kotlinx.serialization.json.* data class User(val name: String, val age: Int) fun main() { - // Creates a Json instance with default settings + // Uses the default Json instance val json = Json // Creates a User object @@ -53,11 +54,29 @@ In addition to using the default configuration, you can [customize the `Json` in such as ignoring unknown keys: ```kotlin +// Imports declarations from the serialization library +import kotlinx.serialization.* +import kotlinx.serialization.json.* + +@Serializable +data class Project(val name: String) + +//sampleStart // Configures a Json instance to ignore unknown keys val customJson = Json { ignoreUnknownKeys = true } + +fun main() { + val data = customJson.decodeFromString(""" + {"name":"kotlinx.serialization","language":"Kotlin"} + """) + println(data) + // Project(name=kotlinx.serialization) +} +//sampleEnd ``` +{kotlin-runnable="true"} ## What's next diff --git a/docs-website/topics/serialization-json-configuration.md b/docs-website/topics/serialization-json-configuration.md index 661750051..d4617e0fa 100644 --- a/docs-website/topics/serialization-json-configuration.md +++ b/docs-website/topics/serialization-json-configuration.md @@ -1,23 +1,27 @@ [//]: # (title: Customize the Json instance) -The default [`Json`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/) class enforces Kotlin type safety and only allows values that can be serialized into standard JSON. +The default [`Json`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/-default/) instance strictly follows the JSON specification and the declarations in Kotlin classes. -You can handle more flexible or non-standard JSON features by creating a custom `Json` instance with the [`Json()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json.html) builder function: +You can use more flexible JSON features or type conversions by creating a custom `Json` instance with the [`Json()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json.html) builder function: ```kotlin -// Creates a Json instance based on the default configuration, ignoring unknown keys +// Creates a Json instance based on the default configuration, allowing special floating-point values val customJson = Json { - ignoreUnknownKeys = true + allowSpecialFloatingPointValues = true } // Use the customJson instance with the same syntax as the default one to encode a string -val jsonString = customJson.encodeToString(user) +val jsonString = customJson.encodeToString(Data(Double.NaN)) println(jsonString) ``` `Json` instances created this way are immutable and thread-safe, which makes them safe to store in a top-level property for reuse. -You can also base a new `Json` instance on an existing one and modify its settings with the [`JsonBuilder`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/) DSL: +> Reusing custom `Json` instances improves performance by allowing them to cache class-specific information. +> +{style="tip"} + +You can also base a new `Json` instance on an existing one and modify its settings using the same builder syntax: ```kotlin // Creates a new instance based on an existing Json @@ -27,10 +31,6 @@ val lenientJson = Json(customJson) { } ``` -> Reusing custom `Json` instances improves performance by allowing them to cache class-specific information. -> -{style="tip"} - The following sections cover the various `Json` class configuration features. ## Customize JSON serialization @@ -47,7 +47,7 @@ You can add indentations and line breaks for better readability by enabling pret To do so, set the [`prettyPrint`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/pretty-print.html) property to `true` in a `Json` instance: ```kotlin -// Imports declarations from the serialization and JSON handling libraries +// Imports declarations from the serialization library import kotlinx.serialization.* import kotlinx.serialization.json.* @@ -77,6 +77,12 @@ This example prints the following result: } ``` +> You can use the [Experimental](components-stability.md#stability-levels-explained) [`prettyPrintIndent`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/pretty-print-indent.html) option to customize the indentation used in pretty-printed JSON. +> +> For example, you can replace the default four spaces with any allowed whitespace character, such as `\t` or `\n`. +> +{style="note"} + ### Encode default values By default, the JSON serializer doesn't encode default property values because they are automatically applied to missing properties during decoding. @@ -86,7 +92,7 @@ For more details, see the [Manage serialization of default properties](serializa To change this default behavior, set the [`encodeDefaults`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/encode-defaults.html) property to `true` in a `Json` instance: ```kotlin -// Imports declarations from the serialization and JSON handling libraries +// Imports declarations from the serialization library import kotlinx.serialization.* import kotlinx.serialization.json.* @@ -118,7 +124,7 @@ By default, all `null` values are encoded into JSON output. To omit `null` values, set the [`explicitNulls`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/explicit-nulls.html) property to `false` in a `Json` instance: ```kotlin -// Imports declarations from the serialization and JSON handling libraries +// Imports declarations from the serialization library import kotlinx.serialization.* import kotlinx.serialization.json.* @@ -166,7 +172,7 @@ The JSON format doesn't natively support maps with structured keys, because JSON To serialize maps with user-defined class keys, use the [`allowStructuredMapKeys`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/allow-structured-map-keys.html) property: ```kotlin -// Imports declarations from the serialization and JSON handling libraries +// Imports declarations from the serialization library import kotlinx.serialization.* import kotlinx.serialization.json.* @@ -199,7 +205,7 @@ and infinities aren't supported in JSON because the JSON specification prohibits To enable their encoding, set the [`allowSpecialFloatingPointValues`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/allow-special-floating-point-values.html) property to `true` in a `Json` instance: ```kotlin -// Imports declarations from the serialization and JSON handling libraries +// Imports declarations from the serialization library import kotlinx.serialization.* import kotlinx.serialization.json.* @@ -230,7 +236,7 @@ Combined with an [explicit serial name defined with the `@SerialName` annotation this approach gives you full control over the resulting JSON structure: ```kotlin -// Imports declarations from the serialization and JSON handling libraries +// Imports declarations from the serialization library import kotlinx.serialization.* import kotlinx.serialization.json.* @@ -269,11 +275,8 @@ fun main() { ``` {kotlin-runnable="true"} -> [`@JsonClassDiscriminator`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-class-discriminator/) is [Experimental](components-stability.md#stability-levels-explained). To opt in, use the `@OptIn(ExperimentalSerializationApi::class)` annotation or the compiler option `-opt-in=kotlinx.serialization.ExperimentalSerializationApi`. -> -{style="warning"} -While the `classDiscriminator` property in a `Json` instance lets you specify a single discriminator key for all polymorphic types, the [`@JsonClassDiscriminator`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-class-discriminator/) annotation offers more flexibility. +While the `classDiscriminator` property in a `Json` instance lets you specify a single discriminator key for all polymorphic types, the [Experimental](components-stability.md#stability-levels-explained) [`@JsonClassDiscriminator`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-class-discriminator/) annotation offers more flexibility. It allows you to define a custom discriminator directly on the base class, which is automatically inherited by all its subclasses. > To learn more about inheritable serial annotations, see [`@InheritableSerialInfo`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-inheritable-serial-info/). @@ -283,7 +286,7 @@ It allows you to define a custom discriminator directly on the base class, which Here's an example: ```kotlin -// Imports declarations from the serialization and JSON handling libraries +// Imports declarations from the serialization library import kotlinx.serialization.* import kotlinx.serialization.json.* @@ -329,7 +332,7 @@ fun main() { > {style="note"} -#### Set class discriminator output mode +### Set class discriminator output mode Use the [`JsonBuilder.classDiscriminatorMode`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/class-discriminator-mode.html) property to control how class discriminators are added to your JSON output. By default, the [discriminator is only added for polymorphic types](#specify-class-discriminator-for-polymorphism), which is useful when working with [polymorphic class hierarchies](serialization-polymorphism.md). @@ -343,7 +346,7 @@ To adjust this behavior, set the [`ClassDiscriminatorMode`](https://kotlinlang.o Here's an example with the `ClassDiscriminatorMode` property set to `NONE`: ```kotlin -// Imports declarations from the serialization and JSON handling libraries +// Imports declarations from the serialization library import kotlinx.serialization.* import kotlinx.serialization.json.* @@ -385,7 +388,7 @@ which requires keys and string literals to be quoted. To relax these restrictions, set the [`isLenient`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/is-lenient.html) property to `true` in a `Json` instance: ```kotlin -// Imports declarations from the serialization and JSON handling libraries +// Imports declarations from the serialization library import kotlinx.serialization.* import kotlinx.serialization.json.* @@ -399,7 +402,7 @@ data class Project(val name: String, val status: Status, val votes: Int) fun main() { // Decodes a JSON string with lenient parsing - // Lenient parsing allows unquoted keys, string and enum values, and quoted integers + // Lenient parsing allows unquoted keys, string and enum values val data = format.decodeFromString(""" { name : kotlinx.serialization, @@ -414,6 +417,73 @@ fun main() { ``` {kotlin-runnable="true"} +#### Allow trailing commas + +To allow trailing commas in JSON input, set the [`allowTrailingComma`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/allow-trailing-comma.html) property to `true`: + +```kotlin +// Imports declarations from the serialization library +import kotlinx.serialization.* +import kotlinx.serialization.json.* + +//sampleStart +// Allows trailing commas in JSON objects and arrays +val format = Json { allowTrailingComma = true } + +fun main() { + val numbers = format.decodeFromString>( + """ + [1, 2, 3,] + """ + ) + println(numbers) + // [1, 2, 3] +} +//sampleEnd +``` +{kotlin-runnable="true"} + +#### Allow comments in JSON + +Use the [`allowComments`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/allow-comments.html) property to allow comments in JSON input. +When this property is enabled, the parser accepts the following comment forms in the input: + +* `//` line comments that end at a newline `\n` +* `/* */` block comments + +> Nested block comments aren't supported +> +{style="note"} + +Here's an example: + +```kotlin +// Imports declarations from the serialization library +import kotlinx.serialization.* +import kotlinx.serialization.json.* + +//sampleStart +// Allows comments in JSON input +val format = Json { allowComments = true } + +fun main() { + val numbers = format.decodeFromString>( + """ + [ + // first element + 1, + /* second element */ + 2 + ] + """ + ) + println(numbers) + // [1, 2] +} +//sampleEnd +``` +{kotlin-runnable="true"} + ### Ignore unknown keys When working with JSON data from third-party services or other dynamic sources, new properties may be added to the JSON objects over time. @@ -423,7 +493,7 @@ To prevent this, set the [`ignoreUnknownKeys`](https://kotlinlang.org/api/kotlin to `true` in a `Json` instance: ```kotlin -// Imports declarations from the serialization and JSON handling libraries +// Imports declarations from the serialization library import kotlinx.serialization.* import kotlinx.serialization.json.* @@ -460,7 +530,7 @@ This lets you keep strict deserialization by default while allowing leniency onl Here's an example: ```kotlin -// Imports declarations from the serialization and JSON handling libraries +// Imports declarations from the serialization library import kotlinx.serialization.* import kotlinx.serialization.json.* @@ -504,7 +574,7 @@ The default `Json` implementation is [strict about input types](serialization-cu To relax this restriction, set the [`coerceInputValues`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/coerce-input-values.html) property to `true` in a `Json` instance: ```kotlin -// Imports declarations from the serialization and JSON handling libraries +// Imports declarations from the serialization library import kotlinx.serialization.* import kotlinx.serialization.json.* @@ -549,7 +619,7 @@ For enums the value is replaced with `null` only if: You can combine `coerceInputValues` with the `explicitNulls` property to handle invalid enum values: ```kotlin -// Imports declarations from the serialization and JSON handling libraries +// Imports declarations from the serialization library import kotlinx.serialization.* import kotlinx.serialization.json.* @@ -590,7 +660,7 @@ However, this prevents decoding data that still uses previous property names. To accept alternative JSON property names for a single property, use the [`@JsonNames`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-names/) annotation: ```kotlin -// Imports declarations from the serialization and JSON handling libraries +// Imports declarations from the serialization library import kotlinx.serialization.* import kotlinx.serialization.json.* @@ -631,7 +701,7 @@ However, JSON data from external sources might use lowercase or mixed-case names To handle such cases, configure the `Json` instance to decode enum values case-insensitively with the [`JsonBuilder.decodeEnumsCaseInsensitive`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/decode-enums-case-insensitive.html) property: ```kotlin -// Imports declarations from the serialization and JSON handling libraries +// Imports declarations from the serialization library import kotlinx.serialization.* import kotlinx.serialization.json.* @@ -667,7 +737,7 @@ For these scenarios, you can specify a global naming strategy using the [JsonBui The Kotlin serialization library provides built-in strategies, such as [JsonNamingStrategy.SnakeCase](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-naming-strategy/-builtins/-snake-case.html): ```kotlin -// Imports declarations from the serialization and JSON handling libraries +// Imports declarations from the serialization library import kotlinx.serialization.* import kotlinx.serialization.json.* From 8082cc1c9bebd8a52791ed76170707ba44697491 Mon Sep 17 00:00:00 2001 From: Daniel Csorba Date: Mon, 24 Nov 2025 16:31:46 +0100 Subject: [PATCH 3/7] feat: adding page for io serialization and implementing review comments --- docs-website/serialization.tree | 1 + .../topics/configure-json-serialization.md | 2 +- .../serialization-json-configuration.md | 27 +++--- .../topics/serialization-json-io-sources.md | 96 +++++++++++++++++++ 4 files changed, 109 insertions(+), 17 deletions(-) create mode 100644 docs-website/topics/serialization-json-io-sources.md diff --git a/docs-website/serialization.tree b/docs-website/serialization.tree index 785272791..ce1b9db18 100644 --- a/docs-website/serialization.tree +++ b/docs-website/serialization.tree @@ -13,6 +13,7 @@ + diff --git a/docs-website/topics/configure-json-serialization.md b/docs-website/topics/configure-json-serialization.md index ce1a0381e..d991db755 100644 --- a/docs-website/topics/configure-json-serialization.md +++ b/docs-website/topics/configure-json-serialization.md @@ -9,7 +9,7 @@ With the `Json` class, you can: * Serialize Kotlin objects to JSON strings using the [`encodeToString()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/encode-to-string.html) function. * Deserialize JSON strings back to Kotlin objects with the [`decodeFromString()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/decode-from-string.html) function. * [Work directly with the `JsonElement`](serialization-json-elements.md) when handling complex JSON structures using the [`encodeToJsonElement()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/encode-to-json-element.html) and the [`decodeFromJsonElement()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/decode-from-json-element.html) functions. -* Use JVM streams with the [`encodeToStream()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/encode-to-stream.html) [`.decodeFromStream()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/decode-from-stream.html), and [`.decodeToSequence()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/decode-to-sequence.html) functions to work with JSON directly from input and output streams. +* Use extension functions, such as [`.encodeToStream()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/encode-to-stream.html), [`.decodeFromStream()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/decode-from-stream.html), or Okio's [`.encodeToSink`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json-io/kotlinx.serialization.json.io/encode-to-sink.html) to [serialize and deserialize JSON directly from I/O sources without creating intermediate strings](serialization-json-io-sources.md). Before you start, import the following declarations from the serialization library: diff --git a/docs-website/topics/serialization-json-configuration.md b/docs-website/topics/serialization-json-configuration.md index d4617e0fa..e47d1b3e8 100644 --- a/docs-website/topics/serialization-json-configuration.md +++ b/docs-website/topics/serialization-json-configuration.md @@ -33,12 +33,6 @@ val lenientJson = Json(customJson) { The following sections cover the various `Json` class configuration features. -## Customize JSON serialization - -You can control how Kotlin objects are serialized to JSON by configuring a `Json` instance. -This allows you to format the output, choose whether to include or omit certain values, -and customize how specific types are encoded. - ### Pretty printing By default, `Json` produces a compact, single-line output. @@ -77,15 +71,20 @@ This example prints the following result: } ``` -> You can use the [Experimental](components-stability.md#stability-levels-explained) [`prettyPrintIndent`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/pretty-print-indent.html) option to customize the indentation used in pretty-printed JSON. +> You can use the [`prettyPrintIndent`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/pretty-print-indent.html) option to customize the indentation used in pretty-printed JSON. +> +> For example, you can replace the default four spaces with any allowed whitespace characters, such as `\t` or `\n`. > -> For example, you can replace the default four spaces with any allowed whitespace character, such as `\t` or `\n`. -> {style="note"} +## Customize JSON structure + +You can customize how a `Json` instance structures the encoded data. +This allows you to control which values appear in the output and how specific types are represented. + ### Encode default values -By default, the JSON serializer doesn't encode default property values because they are automatically applied to missing properties during decoding. +By default, the JSON encoder omits default property values because they are automatically applied to missing properties during decoding. This behavior is especially useful for nullable properties with null defaults, as it avoids writing unnecessary `null` values. For more details, see the [Manage serialization of default properties](serialization-customization-options.md#manage-the-serialization-of-default-properties-with-encodeddefault) section. @@ -417,7 +416,7 @@ fun main() { ``` {kotlin-runnable="true"} -#### Allow trailing commas +### Allow trailing commas To allow trailing commas in JSON input, set the [`allowTrailingComma`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/allow-trailing-comma.html) property to `true`: @@ -443,7 +442,7 @@ fun main() { ``` {kotlin-runnable="true"} -#### Allow comments in JSON +### Allow comments in JSON Use the [`allowComments`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/allow-comments.html) property to allow comments in JSON input. When this property is enabled, the parser accepts the following comment forms in the input: @@ -451,10 +450,6 @@ When this property is enabled, the parser accepts the following comment forms in * `//` line comments that end at a newline `\n` * `/* */` block comments -> Nested block comments aren't supported -> -{style="note"} - Here's an example: ```kotlin diff --git a/docs-website/topics/serialization-json-io-sources.md b/docs-website/topics/serialization-json-io-sources.md new file mode 100644 index 000000000..08cdeb8ae --- /dev/null +++ b/docs-website/topics/serialization-json-io-sources.md @@ -0,0 +1,96 @@ +[//]: # (title: Serialize JSON with I/O sources) + + +The Kotlin serialization library provides APIs for working with JVM streams and Okio's buffered sources and sinks. +You can use these APIs to serialize and deserialize JSON directly from I/O sources without creating intermediate strings. + +## Serialize JSON to JVM output streams + +Use the [`.encodeToStream()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/encode-to-stream.html) extension function to serialize JSON directly to a JVM [`OutputStream`](https://docs.oracle.com/javase/8/docs/api/java/io/OutputStream.html): + +```kotlin +// Imports declarations from the serialization library +import kotlinx.serialization.* +import kotlinx.serialization.json.* +import java.io.FileOutputStream + +@Serializable +data class Project(val name: String, val stars: Int) + +fun main() { + val project = Project("kotlinx.serialization", 9000) + + // Creates an OutputStream for the project.json file + FileOutputStream("project.json").use { output -> + + // Serializes the project instance into the OutputStream + Json.encodeToStream(project, output) + } +} +``` + +In this example, the JSON representation of `Project` is serialized into the `project.json` file. + +## Deserialize JSON from JVM input streams + +To deserialize JSON directly from a JVM [`InputStream`](https://docs.oracle.com/javase/8/docs/api/java/io/InputStream.html), use the [`.decodeFromStream()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/decode-from-stream.html) extension function: + +```kotlin +// Imports declarations from the serialization library +import kotlinx.serialization.* +import kotlinx.serialization.json.* +import java.io.FileInputStream + +@Serializable +data class Project(val name: String, val stars: Int) + +fun main() { + // Opens an InputStream + FileInputStream("project.json").use { input -> + + // Deserializes the JSON contents of the InputStream into a Project instance + val project = Json.decodeFromStream(input) + + // Prints the deserialized Project instance + println(project) + } +} +``` + +In this example, the JSON contents of the input stream are deserialized into a single `Project` instance. + +If your input contains multiple JSON objects in a top-level JSON array, you can use [`.decodeToSequence()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/decode-to-sequence.html) to process the elements lazily. +This lets you handle each value as it is parsed. + +Here's an example: + +```kotlin +// Imports declarations from the serialization library +import kotlinx.serialization.* +import kotlinx.serialization.json.* +import java.io.FileInputStream + +@Serializable +data class Project(val name: String, val stars: Int) + +fun main() { + // Opens an InputStream for the "projects.json" file containing a JSON array of Project objects + FileInputStream("projects.json").use { input -> + + // Lazily deserializes each Project from the InputStream + val projects = Json.decodeToSequence(input) + + // Processes elements one by one + for (project in projects) { + println(project) + } + } +} +``` + +## Serialize JSON with Okio + +[Intro about Okio] +Introduce main use case and functions. (link to [Okio](https://square.github.io/okio/)) + +[Example] \ No newline at end of file From 502b4249ec4b7b2339da31723a65e63d834c0f30 Mon Sep 17 00:00:00 2001 From: Daniel Csorba Date: Thu, 4 Dec 2025 16:03:40 +0100 Subject: [PATCH 4/7] update: created new page for I/O serialization --- .../topics/configure-json-serialization.md | 2 +- .../topics/serialization-json-io-sources.md | 196 +++++++++++++++++- 2 files changed, 191 insertions(+), 7 deletions(-) diff --git a/docs-website/topics/configure-json-serialization.md b/docs-website/topics/configure-json-serialization.md index d991db755..cfba839a8 100644 --- a/docs-website/topics/configure-json-serialization.md +++ b/docs-website/topics/configure-json-serialization.md @@ -9,7 +9,7 @@ With the `Json` class, you can: * Serialize Kotlin objects to JSON strings using the [`encodeToString()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/encode-to-string.html) function. * Deserialize JSON strings back to Kotlin objects with the [`decodeFromString()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/decode-from-string.html) function. * [Work directly with the `JsonElement`](serialization-json-elements.md) when handling complex JSON structures using the [`encodeToJsonElement()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/encode-to-json-element.html) and the [`decodeFromJsonElement()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/decode-from-json-element.html) functions. -* Use extension functions, such as [`.encodeToStream()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/encode-to-stream.html), [`.decodeFromStream()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/decode-from-stream.html), or Okio's [`.encodeToSink`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json-io/kotlinx.serialization.json.io/encode-to-sink.html) to [serialize and deserialize JSON directly from I/O sources without creating intermediate strings](serialization-json-io-sources.md). +* Use extension functions to [serialize and deserialize I/O sources](serialization-json-io-sources.md) without creating intermediate strings, including JVM streams as well as [`kotlinx-io`](https://github.com/Kotlin/kotlinx-io) and [Okio](https://square.github.io/okio/) types. Before you start, import the following declarations from the serialization library: diff --git a/docs-website/topics/serialization-json-io-sources.md b/docs-website/topics/serialization-json-io-sources.md index 08cdeb8ae..39c18bc12 100644 --- a/docs-website/topics/serialization-json-io-sources.md +++ b/docs-website/topics/serialization-json-io-sources.md @@ -1,9 +1,14 @@ [//]: # (title: Serialize JSON with I/O sources) -The Kotlin serialization library provides APIs for working with JVM streams and Okio's buffered sources and sinks. +The Kotlin serialization library provides APIs for working with JVM streams and [`kotlinx-io`](https://github.com/Kotlin/kotlinx-io) or [Okio](https://square.github.io/okio/) sources and sinks. You can use these APIs to serialize and deserialize JSON directly from I/O sources without creating intermediate strings. +> When working with I/O resources, it's important to close them properly to prevent resource leaks. +> You can do this with the `.use()` function, which closes the resource automatically when the operation completes. +> +{style="note"} + ## Serialize JSON to JVM output streams Use the [`.encodeToStream()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/encode-to-stream.html) extension function to serialize JSON directly to a JVM [`OutputStream`](https://docs.oracle.com/javase/8/docs/api/java/io/OutputStream.html): @@ -74,7 +79,7 @@ import java.io.FileInputStream data class Project(val name: String, val stars: Int) fun main() { - // Opens an InputStream for the "projects.json" file containing a JSON array of Project objects + // Opens an InputStream for the projects.json file containing a JSON array of Project objects FileInputStream("projects.json").use { input -> // Lazily deserializes each Project from the InputStream @@ -88,9 +93,188 @@ fun main() { } ``` -## Serialize JSON with Okio +> You can iterate through sequences returned by `.decodeToSequence()` only once +> because they are tied to the underlying stream. +> +{style="note"} + +## JSON serialization with kotlinx-io and Okio + +In addition to JVM streams, you can work with JSON using I/O types, such as `Source` and `Sink` from [`kotlinx-io`](https://github.com/Kotlin/kotlinx-io), and `BufferedSource` +and `BufferedSink` from [Okio](https://square.github.io/okio/). + +You can use the following `Json` extension functions to read and write JSON directly through these I/O types: + +* [`.encodeToSink()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json-io/kotlinx.serialization.json.io/encode-to-sink.html) and [`.encodeToBufferedSink()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json-okio/kotlinx.serialization.json.okio/encode-to-buffered-sink.html) to write JSON to a `Sink` or `BufferedSink`. +* [`.decodeFromSource()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json-io/kotlinx.serialization.json.io/decode-from-source.html) and [`.decodeFromBufferedSource()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json-okio/kotlinx.serialization.json.okio/decode-from-buffered-source.html) to read a single JSON value from a `Source` or `BufferedSource`. +* [`.decodeSourceToSequence()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json-io/kotlinx.serialization.json.io/decode-source-to-sequence.html) and [`.decodeBufferedSourceToSequence()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json-okio/kotlinx.serialization.json.okio/decode-buffered-source-to-sequence.html) to lazily decode multiple JSON values as a `Sequence`. + +> The JSON I/O extension functions use UTF-8 encoding and throw `SerializationException` for invalid JSON data and `IOException` for I/O failures. +> +{style="tip"} -[Intro about Okio] -Introduce main use case and functions. (link to [Okio](https://square.github.io/okio/)) +To use these extension functions with `kotlinx-io` or Okio types, add the following dependencies to your project: + + + + +```kotlin +// build.gradle.kts +dependencies { + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json-io:%serializationVersion%") + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json-okio:%serializationVersion%") + + implementation("org.jetbrains.kotlinx:kotlinx-io-core:%kotlinxIoVersion%") + implementation("com.squareup.okio:okio:%okioVersion%") +} + +``` + + + + +```groovy +// build.gradle +dependencies { + implementation "org.jetbrains.kotlinx:kotlinx-serialization-json-io:%serializationVersion%" + implementation "org.jetbrains.kotlinx:kotlinx-serialization-json-okio:%serializationVersion%" + + implementation "org.jetbrains.kotlinx:kotlinx-io-core:%kotlinxIoVersion%" + implementation "com.squareup.okio:okio:%okioVersion%" +} +``` + + + + +```xml + + + + org.jetbrains.kotlinx + kotlinx-serialization-json-io + %serializationVersion% + + + org.jetbrains.kotlinx + kotlinx-serialization-json-okio + %serializationVersion% + + + org.jetbrains.kotlinx + kotlinx-io-core + %kotlinxIoVersion% + + + com.squareup.okio + okio + %okioVersion% + + +``` + + + +The next sections cover examples using `kotlinx-io` types with these APIs. +You can use Okio types similarly with their corresponding `BufferedSource` and `BufferedSink`. + + +### Serialize JSON to Sinks + +Use the `.encodeToSink()` function to serialize JSON to a `Sink`: + +```kotlin +// Imports declarations from the serialization library +import kotlinx.serialization.* +import kotlinx.serialization.json.* + +// Imports declarations for kotlinx-io types and JSON I/O support +import kotlinx.serialization.json.io.* +import kotlinx.io.* +import kotlinx.io.files.* + +@Serializable +data class Project(val name: String, val stars: Int) + +@OptIn(ExperimentalSerializationApi::class) +fun main() { + val project = Project("kotlinx.serialization", 9000) + + // Creates a Sink for the project.json file + val path = Path("project.json") + SystemFileSystem.sink(path).buffered().use { sink: Sink -> + + // Serializes the Project instance directly into a Sink + Json.encodeToSink(project, sink) + } +} +``` + +### Deserialize JSON from Sources + +To deserialize JSON from a `Source`, use the `.decodeFromSource()` function: + +```kotlin +// Imports declarations from the serialization library +import kotlinx.serialization.* +import kotlinx.serialization.json.* + +// Imports declarations for kotlinx-io types and JSON I/O support +import kotlinx.serialization.json.io.* +import kotlinx.io.* +import kotlinx.io.files.* + +@Serializable +data class Project(val name: String, val stars: Int) + +@OptIn(ExperimentalSerializationApi::class) +fun main() { + // Opens a Source for the project.json file + val path = Path("project.json") + SystemFileSystem.source(path).buffered().use { source: Source -> + + // Deserializes a Project instance directly from a Source + val project = Json.decodeFromSource(source) + + println(project) + } +} +``` + +If your input contains a large JSON array or multiple top-level JSON objects, +you can turn a `Source` into a lazily decoded `Sequence` with the +`.decodeSourceToSequence()` function. + +```kotlin +// Imports declarations from the serialization library +import kotlinx.serialization.* +import kotlinx.serialization.json.* + +// Imports declarations for kotlinx-io types and JSON I/O support +import kotlinx.serialization.json.io.* +import kotlinx.io.* +import kotlinx.io.files.* + +@Serializable +data class Project(val name: String, val stars: Int) + +@OptIn(ExperimentalSerializationApi::class) +fun main() { + // Opens a Source for the projects.json file containing multiple JSON objects + val path = Path("projects.json") + SystemFileSystem.source(path).buffered().use { source: Source -> + + // Lazily deserializes each Project as it is read from the Source + val projects: Sequence = Json.decodeSourceToSequence(source) + + for (project in projects) { + println(project) + } + } +} +``` -[Example] \ No newline at end of file +> You can iterate through sequences returned by `.decodeSourceToSequence()` only once +> because they are tied to the underlying `Source`. +> +{style="note"} From 34b33df033a83ee4a14db030c4a97af270a2ddcf Mon Sep 17 00:00:00 2001 From: Daniel Csorba Date: Thu, 4 Dec 2025 16:37:38 +0100 Subject: [PATCH 5/7] update: implementing additional review comments --- .../topics/configure-json-serialization.md | 2 +- .../serialization-json-configuration.md | 258 +++++++++--------- 2 files changed, 130 insertions(+), 130 deletions(-) diff --git a/docs-website/topics/configure-json-serialization.md b/docs-website/topics/configure-json-serialization.md index cfba839a8..f1f20aaab 100644 --- a/docs-website/topics/configure-json-serialization.md +++ b/docs-website/topics/configure-json-serialization.md @@ -58,10 +58,10 @@ such as ignoring unknown keys: import kotlinx.serialization.* import kotlinx.serialization.json.* +//sampleStart @Serializable data class Project(val name: String) -//sampleStart // Configures a Json instance to ignore unknown keys val customJson = Json { ignoreUnknownKeys = true diff --git a/docs-website/topics/serialization-json-configuration.md b/docs-website/topics/serialization-json-configuration.md index e47d1b3e8..75b6558a8 100644 --- a/docs-website/topics/serialization-json-configuration.md +++ b/docs-website/topics/serialization-json-configuration.md @@ -33,53 +33,9 @@ val lenientJson = Json(customJson) { The following sections cover the various `Json` class configuration features. -### Pretty printing - -By default, `Json` produces a compact, single-line output. - -You can add indentations and line breaks for better readability by enabling pretty printing in the output. -To do so, set the [`prettyPrint`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/pretty-print.html) property to `true` in a `Json` instance: - -```kotlin -// Imports declarations from the serialization library -import kotlinx.serialization.* -import kotlinx.serialization.json.* - -//sampleStart -// Creates a custom Json format -val format = Json { prettyPrint = true } - -@Serializable -data class Project(val name: String, val language: String) - -fun main() { - val data = Project("kotlinx.serialization", "Kotlin") - - // Prints the JSON output with line breaks and indentations - println(format.encodeToString(data)) -} -//sampleEnd -``` -{kotlin-runnable="true"} - -This example prints the following result: - -```text -{ - "name": "kotlinx.serialization", - "language": "Kotlin" -} -``` - -> You can use the [`prettyPrintIndent`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/pretty-print-indent.html) option to customize the indentation used in pretty-printed JSON. -> -> For example, you can replace the default four spaces with any allowed whitespace characters, such as `\t` or `\n`. -> -{style="note"} - ## Customize JSON structure -You can customize how a `Json` instance structures the encoded data. +You can customize how a `Json` instance structures data during encoding and decoding. This allows you to control which values appear in the output and how specific types are represented. ### Encode default values @@ -165,10 +121,10 @@ In this example, the `version` property is `null` before encoding but decodes to > {style="tip"} -### Encode structured map keys +### Allow structured map keys The JSON format doesn't natively support maps with structured keys, because JSON keys are strings that represent only primitives or enums. -To serialize maps with user-defined class keys, use the [`allowStructuredMapKeys`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/allow-structured-map-keys.html) property: +To serialize and deserialize maps with user-defined class keys, use the [`allowStructuredMapKeys`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/allow-structured-map-keys.html) property: ```kotlin // Imports declarations from the serialization library @@ -196,12 +152,12 @@ fun main() { ``` {kotlin-runnable="true"} -### Encode special floating-point values +### Allow special floating-point values By default, special floating-point values like [`Double.NaN`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-double/-na-n.html) and infinities aren't supported in JSON because the JSON specification prohibits them. -To enable their encoding, set the [`allowSpecialFloatingPointValues`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/allow-special-floating-point-values.html) property to `true` in a `Json` instance: +To enable their encoding and decoding, set the [`allowSpecialFloatingPointValues`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/allow-special-floating-point-values.html) property to `true` in a `Json` instance: ```kotlin // Imports declarations from the serialization library @@ -379,6 +335,86 @@ fun main() { Kotlin's `Json` parser provides several settings that let you customize how JSON data is parsed and deserialized. +### Coerce input values + +When working with JSON data from third-party services or other dynamic sources, the format can evolve over time. +This may cause exceptions during decoding when actual values don't match the expected types. + +The default `Json` implementation is [strict about input types](serialization-customization-options.md#the-serializable-annotation). +To relax this restriction, set the [`coerceInputValues`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/coerce-input-values.html) property to `true` in a `Json` instance: + +```kotlin +// Imports declarations from the serialization library +import kotlinx.serialization.* +import kotlinx.serialization.json.* + +//sampleStart +val format = Json { coerceInputValues = true } + +@Serializable +data class Project(val name: String, val language: String = "Kotlin") + +fun main() { + val data = format.decodeFromString(""" + {"name":"kotlinx.serialization","language":null} + """) + + // Coerces the invalid null value for language to its default value + println(data) + // Project(name=kotlinx.serialization, language=Kotlin) +} +//sampleEnd +``` +{kotlin-runnable="true"} + +The `coerceInputValues` property only affects decoding. It treats certain invalid input values as if the corresponding property were missing. +Currently, it applies to: + +* `null` inputs for non-nullable types +* unknown values for enums + +> This list may be expanded in future versions, making `Json` instances with this property even more permissive +> by replacing invalid values with defaults or `null`. +> +{style="note"} + +If a value is missing, it's replaced with a default property value if one exists. + +For enums the value is replaced with `null` only if: + +* No default is defined. +* The [`explicitNulls`](#omit-explicit-nulls) property is set to `false`. +* The property is nullable. + +You can combine `coerceInputValues` with the `explicitNulls` property to handle invalid enum values: + +```kotlin +// Imports declarations from the serialization library +import kotlinx.serialization.* +import kotlinx.serialization.json.* + +//sampleStart +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() { + + // Coerces the unknown foreground value to its default and background to null + val brush = json.decodeFromString("""{"foreground":"pink", "background":"purple"}""") + println(brush) + // Brush(foreground=BLACK, background=null) +} +//sampleEnd +``` +{kotlin-runnable="true"} + ### Lenient parsing By default, the `Json` parser enforces strict JSON rules to ensure compliance with the [RFC-4627](https://www.ietf.org/rfc/rfc4627.txt) specification, @@ -560,86 +596,6 @@ fun main() { In this example, `Inner` throws a `SerializationException` for unknown keys because it isn't annotated with `@JsonIgnoreUnknownKeys`. -### Coerce input values - -When working with JSON data from third-party services or other dynamic sources, the format can evolve over time. -This may cause exceptions during decoding when actual values don't match the expected types. - -The default `Json` implementation is [strict about input types](serialization-customization-options.md#the-serializable-annotation). -To relax this restriction, set the [`coerceInputValues`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/coerce-input-values.html) property to `true` in a `Json` instance: - -```kotlin -// Imports declarations from the serialization library -import kotlinx.serialization.* -import kotlinx.serialization.json.* - -//sampleStart -val format = Json { coerceInputValues = true } - -@Serializable -data class Project(val name: String, val language: String = "Kotlin") - -fun main() { - val data = format.decodeFromString(""" - {"name":"kotlinx.serialization","language":null} - """) - - // Coerces the invalid null value for language to its default value - println(data) - // Project(name=kotlinx.serialization, language=Kotlin) -} -//sampleEnd -``` -{kotlin-runnable="true"} - -The `coerceInputValues` property only affects decoding. It treats certain invalid input values as if the corresponding property were missing. -Currently, it applies to: - -* `null` inputs for non-nullable types -* unknown values for enums - -> This list may be expanded in future versions, making `Json` instances with this property even more permissive -> by replacing invalid values with defaults or `null`. -> -{style="note"} - -If a value is missing, it's replaced with a default property value if one exists. - -For enums the value is replaced with `null` only if: - -* No default is defined. -* The [`explicitNulls`](#omit-explicit-nulls) property is set to `false`. -* The property is nullable. - -You can combine `coerceInputValues` with the `explicitNulls` property to handle invalid enum values: - -```kotlin -// Imports declarations from the serialization library -import kotlinx.serialization.* -import kotlinx.serialization.json.* - -//sampleStart -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() { - - // Coerces the unknown foreground value to its default and background to null - val brush = json.decodeFromString("""{"foreground":"pink", "background":"purple"}""") - println(brush) - // Brush(foreground=BLACK, background=null) -} -//sampleEnd -``` -{kotlin-runnable="true"} - ## Customize name mapping between JSON and Kotlin Some JSON data may not perfectly align with Kotlin's naming conventions or expected formats. @@ -766,6 +722,50 @@ When using a global naming strategy with [`JsonNamingStrategy`](https://kotlinla Given these factors, consider the trade-offs carefully before implementing global naming strategies in your application. +## Pretty printing + +By default, `Json` produces a compact, single-line output. + +You can add indentations and line breaks for better readability by enabling pretty printing in the output. +To do so, set the [`prettyPrint`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/pretty-print.html) property to `true` in a `Json` instance: + +```kotlin +// Imports declarations from the serialization library +import kotlinx.serialization.* +import kotlinx.serialization.json.* + +//sampleStart +// Creates a custom Json format +val format = Json { prettyPrint = true } + +@Serializable +data class Project(val name: String, val language: String) + +fun main() { + val data = Project("kotlinx.serialization", "Kotlin") + + // Prints the JSON output with line breaks and indentations + println(format.encodeToString(data)) +} +//sampleEnd +``` +{kotlin-runnable="true"} + +This example prints the following result: + +```text +{ + "name": "kotlinx.serialization", + "language": "Kotlin" +} +``` + +> You can use the [`prettyPrintIndent`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/pretty-print-indent.html) option to customize the indentation used in pretty-printed JSON. +> +> For example, you can replace the default four spaces with any allowed whitespace characters, such as `\t` or `\n`. +> +{style="note"} + ## What's next * Explore [advanced JSON element handling](serialization-json-elements.md) to manipulate and work with JSON data before it's parsed or serialized. From 0ef93d79b2ed7756b47dea94bfc28cbde8c666e2 Mon Sep 17 00:00:00 2001 From: Daniel Csorba Date: Fri, 5 Dec 2025 10:32:32 +0100 Subject: [PATCH 6/7] update: small update to section ordering --- .../topics/configure-json-serialization.md | 2 +- .../serialization-json-configuration.md | 282 +++++++++--------- 2 files changed, 142 insertions(+), 142 deletions(-) diff --git a/docs-website/topics/configure-json-serialization.md b/docs-website/topics/configure-json-serialization.md index f1f20aaab..4ecd80c84 100644 --- a/docs-website/topics/configure-json-serialization.md +++ b/docs-website/topics/configure-json-serialization.md @@ -9,7 +9,7 @@ With the `Json` class, you can: * Serialize Kotlin objects to JSON strings using the [`encodeToString()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/encode-to-string.html) function. * Deserialize JSON strings back to Kotlin objects with the [`decodeFromString()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/decode-from-string.html) function. * [Work directly with the `JsonElement`](serialization-json-elements.md) when handling complex JSON structures using the [`encodeToJsonElement()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/encode-to-json-element.html) and the [`decodeFromJsonElement()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/decode-from-json-element.html) functions. -* Use extension functions to [serialize and deserialize I/O sources](serialization-json-io-sources.md) without creating intermediate strings, including JVM streams as well as [`kotlinx-io`](https://github.com/Kotlin/kotlinx-io) and [Okio](https://square.github.io/okio/) types. +* Use [Experimental](components-stability.md#stability-levels-explained) extension functions to [serialize and deserialize I/O sources](serialization-json-io-sources.md) without creating intermediate strings, including JVM streams as well as [`kotlinx-io`](https://github.com/Kotlin/kotlinx-io) and [Okio](https://square.github.io/okio/) types. Before you start, import the following declarations from the serialization library: diff --git a/docs-website/topics/serialization-json-configuration.md b/docs-website/topics/serialization-json-configuration.md index 75b6558a8..bd333cb15 100644 --- a/docs-website/topics/serialization-json-configuration.md +++ b/docs-website/topics/serialization-json-configuration.md @@ -331,10 +331,135 @@ fun main() { > {style="note"} +### Pretty printing + +By default, `Json` produces a compact, single-line output. + +You can add indentations and line breaks for better readability by enabling pretty printing in the output. +To do so, set the [`prettyPrint`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/pretty-print.html) property to `true` in a `Json` instance: + +```kotlin +// Imports declarations from the serialization library +import kotlinx.serialization.* +import kotlinx.serialization.json.* + +//sampleStart +// Creates a custom Json format +val format = Json { prettyPrint = true } + +@Serializable +data class Project(val name: String, val language: String) + +fun main() { + val data = Project("kotlinx.serialization", "Kotlin") + + // Prints the JSON output with line breaks and indentations + println(format.encodeToString(data)) +} +//sampleEnd +``` +{kotlin-runnable="true"} + +This example prints the following result: + +```text +{ + "name": "kotlinx.serialization", + "language": "Kotlin" +} +``` + +> You can use the [`prettyPrintIndent`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/pretty-print-indent.html) option to customize the indentation used in pretty-printed JSON. +> +> For example, you can replace the default four spaces with any allowed whitespace characters, such as `\t` or `\n`. +> +{style="note"} + ## Customize JSON deserialization Kotlin's `Json` parser provides several settings that let you customize how JSON data is parsed and deserialized. +### Ignore unknown keys + +When working with JSON data from third-party services or other dynamic sources, new properties may be added to the JSON objects over time. + +By default, unknown keys (the property names in the JSON input) result in an error during deserialization. +To prevent this, set the [`ignoreUnknownKeys`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/ignore-unknown-keys.html) property +to `true` in a `Json` instance: + +```kotlin +// Imports declarations from the serialization library +import kotlinx.serialization.* +import kotlinx.serialization.json.* + +//sampleStart +// Creates a Json instance to ignore unknown keys +val format = Json { ignoreUnknownKeys = true } + +@Serializable +data class Project(val name: String) + +fun main() { + val data = format.decodeFromString(""" + {"name":"kotlinx.serialization","language":"Kotlin"} + """) + // The language key is ignored because it's not in the Project class + println(data) + // Project(name=kotlinx.serialization) +} +//sampleEnd +``` +{kotlin-runnable="true"} + +#### Ignore unknown keys for specific classes + + +> This feature is [Experimental](components-stability.md#stability-levels-explained). To opt in, use the `@OptIn(ExperimentalSerializationApi::class)` annotation or the compiler option `-opt-in=kotlinx.serialization.ExperimentalSerializationApi`. +> +{style="warning"} + +Instead of [enabling `ignoreUnknownKeys` for all classes](#ignore-unknown-keys), +you can ignore unknown keys only for specific classes by annotating them with the `@JsonIgnoreUnknownKeys` annotation. +This lets you keep strict deserialization by default while allowing leniency only where you need it. + +Here's an example: + +```kotlin +// Imports declarations from the serialization library +import kotlinx.serialization.* +import kotlinx.serialization.json.* + +//sampleStart +@OptIn(ExperimentalSerializationApi::class) +@Serializable +// Unknown properties in Outer are ignored during deserialization +@JsonIgnoreUnknownKeys +data class Outer(val a: Int, val inner: Inner) + +@Serializable +data class Inner(val x: String) + +fun main() { + val outer = Json.decodeFromString( + """{"a":1,"inner":{"x":"value"},"unknownKey":42}""" + ) + println(outer) + // Outer(a=1, inner=Inner(x=value)) + + // Throws an exception + // unknownKey inside inner is NOT ignored because Inner is not annotated + println( + Json.decodeFromString( + """{"a":1,"inner":{"x":"value","unknownKey":"unexpected"}}""" + ) + ) +} +//sampleEnd +``` +{kotlin-runnable="true" min-compiler-version="2.2" validate="false"} + +In this example, `Inner` throws a `SerializationException` for unknown keys because it isn't annotated with `@JsonIgnoreUnknownKeys`. + ### Coerce input values When working with JSON data from third-party services or other dynamic sources, the format can evolve over time. @@ -415,43 +540,6 @@ fun main() { ``` {kotlin-runnable="true"} -### Lenient parsing - -By default, the `Json` parser enforces strict JSON rules to ensure compliance with the [RFC-4627](https://www.ietf.org/rfc/rfc4627.txt) specification, -which requires keys and string literals to be quoted. - -To relax these restrictions, set the [`isLenient`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/is-lenient.html) property to `true` in a `Json` instance: - -```kotlin -// Imports declarations from the serialization library -import kotlinx.serialization.* -import kotlinx.serialization.json.* - -//sampleStart -val format = Json { isLenient = true } - -enum class Status { SUPPORTED } - -@Serializable -data class Project(val name: String, val status: Status, val votes: Int) - -fun main() { - // Decodes a JSON string with lenient parsing - // Lenient parsing allows unquoted keys, string and enum values - val data = format.decodeFromString(""" - { - name : kotlinx.serialization, - status : SUPPORTED, - votes : "9000" - } - """) - println(data) - // Project(name=kotlinx.serialization, status=SUPPORTED, votes=9000) -} -//sampleEnd -``` -{kotlin-runnable="true"} - ### Allow trailing commas To allow trailing commas in JSON input, set the [`allowTrailingComma`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/allow-trailing-comma.html) property to `true`: @@ -515,13 +603,12 @@ fun main() { ``` {kotlin-runnable="true"} -### Ignore unknown keys +### Lenient parsing -When working with JSON data from third-party services or other dynamic sources, new properties may be added to the JSON objects over time. +By default, the `Json` parser enforces strict JSON rules to ensure compliance with the [RFC-4627](https://www.ietf.org/rfc/rfc4627.txt) specification, +which requires keys and string literals to be quoted. -By default, unknown keys (the property names in the JSON input) result in an error during deserialization. -To prevent this, set the [`ignoreUnknownKeys`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/ignore-unknown-keys.html) property -to `true` in a `Json` instance: +To relax these restrictions, set the [`isLenient`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/is-lenient.html) property to `true` in a `Json` instance: ```kotlin // Imports declarations from the serialization library @@ -529,73 +616,30 @@ import kotlinx.serialization.* import kotlinx.serialization.json.* //sampleStart -// Creates a Json instance to ignore unknown keys -val format = Json { ignoreUnknownKeys = true } +val format = Json { isLenient = true } + +enum class Status { SUPPORTED } @Serializable -data class Project(val name: String) +data class Project(val name: String, val status: Status, val votes: Int) fun main() { + // Decodes a JSON string with lenient parsing + // Lenient parsing allows unquoted keys, string and enum values val data = format.decodeFromString(""" - {"name":"kotlinx.serialization","language":"Kotlin"} + { + name : kotlinx.serialization, + status : SUPPORTED, + votes : "9000" + } """) - // The language key is ignored because it's not in the Project class println(data) - // Project(name=kotlinx.serialization) + // Project(name=kotlinx.serialization, status=SUPPORTED, votes=9000) } //sampleEnd ``` {kotlin-runnable="true"} -#### Ignore unknown keys for specific classes - - -> This feature is [Experimental](components-stability.md#stability-levels-explained). To opt in, use the `@OptIn(ExperimentalSerializationApi::class)` annotation or the compiler option `-opt-in=kotlinx.serialization.ExperimentalSerializationApi`. -> -{style="warning"} - -Instead of [enabling `ignoreUnknownKeys` for all classes](#ignore-unknown-keys), -you can ignore unknown keys only for specific classes by annotating them with the `@JsonIgnoreUnknownKeys` annotation. -This lets you keep strict deserialization by default while allowing leniency only where you need it. - -Here's an example: - -```kotlin -// Imports declarations from the serialization library -import kotlinx.serialization.* -import kotlinx.serialization.json.* - -//sampleStart -@OptIn(ExperimentalSerializationApi::class) -@Serializable -// Unknown properties in Outer are ignored during deserialization -@JsonIgnoreUnknownKeys -data class Outer(val a: Int, val inner: Inner) - -@Serializable -data class Inner(val x: String) - -fun main() { - val outer = Json.decodeFromString( - """{"a":1,"inner":{"x":"value"},"unknownKey":42}""" - ) - println(outer) - // Outer(a=1, inner=Inner(x=value)) - - // Throws an exception - // unknownKey inside inner is NOT ignored because Inner is not annotated - println( - Json.decodeFromString( - """{"a":1,"inner":{"x":"value","unknownKey":"unexpected"}}""" - ) - ) -} -//sampleEnd -``` -{kotlin-runnable="true" min-compiler-version="2.2" validate="false"} - -In this example, `Inner` throws a `SerializationException` for unknown keys because it isn't annotated with `@JsonIgnoreUnknownKeys`. - ## Customize name mapping between JSON and Kotlin Some JSON data may not perfectly align with Kotlin's naming conventions or expected formats. @@ -722,50 +766,6 @@ When using a global naming strategy with [`JsonNamingStrategy`](https://kotlinla Given these factors, consider the trade-offs carefully before implementing global naming strategies in your application. -## Pretty printing - -By default, `Json` produces a compact, single-line output. - -You can add indentations and line breaks for better readability by enabling pretty printing in the output. -To do so, set the [`prettyPrint`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/pretty-print.html) property to `true` in a `Json` instance: - -```kotlin -// Imports declarations from the serialization library -import kotlinx.serialization.* -import kotlinx.serialization.json.* - -//sampleStart -// Creates a custom Json format -val format = Json { prettyPrint = true } - -@Serializable -data class Project(val name: String, val language: String) - -fun main() { - val data = Project("kotlinx.serialization", "Kotlin") - - // Prints the JSON output with line breaks and indentations - println(format.encodeToString(data)) -} -//sampleEnd -``` -{kotlin-runnable="true"} - -This example prints the following result: - -```text -{ - "name": "kotlinx.serialization", - "language": "Kotlin" -} -``` - -> You can use the [`prettyPrintIndent`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/pretty-print-indent.html) option to customize the indentation used in pretty-printed JSON. -> -> For example, you can replace the default four spaces with any allowed whitespace characters, such as `\t` or `\n`. -> -{style="note"} - ## What's next * Explore [advanced JSON element handling](serialization-json-elements.md) to manipulate and work with JSON data before it's parsed or serialized. From 83119debf90047ef627a363154e605fc3fa3f9f2 Mon Sep 17 00:00:00 2001 From: Daniel Csorba Date: Tue, 9 Dec 2025 17:17:52 +0100 Subject: [PATCH 7/7] update: implementing comments related to the IO sources page --- .../serialization-json-configuration.md | 4 +- .../topics/serialization-json-io-sources.md | 127 ++++++++++++++---- 2 files changed, 102 insertions(+), 29 deletions(-) diff --git a/docs-website/topics/serialization-json-configuration.md b/docs-website/topics/serialization-json-configuration.md index bd333cb15..253d5ddd4 100644 --- a/docs-website/topics/serialization-json-configuration.md +++ b/docs-website/topics/serialization-json-configuration.md @@ -42,7 +42,7 @@ This allows you to control which values appear in the output and how specific ty By default, the JSON encoder omits default property values because they are automatically applied to missing properties during decoding. This behavior is especially useful for nullable properties with null defaults, as it avoids writing unnecessary `null` values. -For more details, see the [Manage serialization of default properties](serialization-customization-options.md#manage-the-serialization-of-default-properties-with-encodeddefault) section. +For more details, see the [Manage serialization of default properties](serialization-customization-options.md#manage-the-serialization-of-default-properties-with-encodedefault) section. To change this default behavior, set the [`encodeDefaults`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/encode-defaults.html) property to `true` in a `Json` instance: @@ -605,7 +605,7 @@ fun main() { ### Lenient parsing -By default, the `Json` parser enforces strict JSON rules to ensure compliance with the [RFC-4627](https://www.ietf.org/rfc/rfc4627.txt) specification, +By default, the `Json` parser enforces strict JSON rules to ensure compliance with the [RFC-8259](https://www.ietf.org/rfc/rfc8259.txt) specification, which requires keys and string literals to be quoted. To relax these restrictions, set the [`isLenient`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/is-lenient.html) property to `true` in a `Json` instance: diff --git a/docs-website/topics/serialization-json-io-sources.md b/docs-website/topics/serialization-json-io-sources.md index 39c18bc12..9e044099b 100644 --- a/docs-website/topics/serialization-json-io-sources.md +++ b/docs-website/topics/serialization-json-io-sources.md @@ -3,6 +3,7 @@ The Kotlin serialization library provides APIs for working with JVM streams and [`kotlinx-io`](https://github.com/Kotlin/kotlinx-io) or [Okio](https://square.github.io/okio/) sources and sinks. You can use these APIs to serialize and deserialize JSON directly from I/O sources without creating intermediate strings. +These APIs use UTF-8 encoding and throw `SerializationException` for invalid JSON data and `IOException` for I/O failures. > When working with I/O resources, it's important to close them properly to prevent resource leaks. > You can do this with the `.use()` function, which closes the resource automatically when the operation completes. @@ -64,7 +65,7 @@ fun main() { In this example, the JSON contents of the input stream are deserialized into a single `Project` instance. -If your input contains multiple JSON objects in a top-level JSON array, you can use [`.decodeToSequence()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/decode-to-sequence.html) to process the elements lazily. +If your input contains multiple JSON objects in a top-level JSON array or as whitespace-separated objects, you can use [`.decodeToSequence()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/decode-to-sequence.html) to process the elements lazily. This lets you handle each value as it is parsed. Here's an example: @@ -96,56 +97,54 @@ fun main() { > You can iterate through sequences returned by `.decodeToSequence()` only once > because they are tied to the underlying stream. > +> Closing the stream before the sequence is fully evaluated causes an `IOException`. +> {style="note"} ## JSON serialization with kotlinx-io and Okio -In addition to JVM streams, you can work with JSON using I/O types, such as `Source` and `Sink` from [`kotlinx-io`](https://github.com/Kotlin/kotlinx-io), and `BufferedSource` -and `BufferedSink` from [Okio](https://square.github.io/okio/). +In addition to JVM streams, you can work with JSON using I/O types, such as `kotlinx.io.Sink` and `kotlinx.io.Source` from [`kotlinx-io`](https://github.com/Kotlin/kotlinx-io) (currently in [Alpha](components-stability.md#stability-levels-explained)), and `okio.BufferedSink` +and `okio.BufferedSource` from [Okio](https://square.github.io/okio/). You can use the following `Json` extension functions to read and write JSON directly through these I/O types: -* [`.encodeToSink()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json-io/kotlinx.serialization.json.io/encode-to-sink.html) and [`.encodeToBufferedSink()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json-okio/kotlinx.serialization.json.okio/encode-to-buffered-sink.html) to write JSON to a `Sink` or `BufferedSink`. -* [`.decodeFromSource()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json-io/kotlinx.serialization.json.io/decode-from-source.html) and [`.decodeFromBufferedSource()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json-okio/kotlinx.serialization.json.okio/decode-from-buffered-source.html) to read a single JSON value from a `Source` or `BufferedSource`. +* [`.encodeToSink()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json-io/kotlinx.serialization.json.io/encode-to-sink.html) and [`.encodeToBufferedSink()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json-okio/kotlinx.serialization.json.okio/encode-to-buffered-sink.html) to write JSON to a `kotlinx.io.Sink` or `okio.BufferedSink`. +* [`.decodeFromSource()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json-io/kotlinx.serialization.json.io/decode-from-source.html) and [`.decodeFromBufferedSource()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json-okio/kotlinx.serialization.json.okio/decode-from-buffered-source.html) to read a single JSON value from a `kotlinx.io.Source` or `okio.BufferedSource`. * [`.decodeSourceToSequence()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json-io/kotlinx.serialization.json.io/decode-source-to-sequence.html) and [`.decodeBufferedSourceToSequence()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json-okio/kotlinx.serialization.json.okio/decode-buffered-source-to-sequence.html) to lazily decode multiple JSON values as a `Sequence`. -> The JSON I/O extension functions use UTF-8 encoding and throw `SerializationException` for invalid JSON data and `IOException` for I/O failures. -> -{style="tip"} +The next sections cover examples using `kotlinx-io` types with these APIs. +You can use Okio types similarly with their corresponding `okio.BufferedSink` and `okio.BufferedSource`. -To use these extension functions with `kotlinx-io` or Okio types, add the following dependencies to your project: +### Add dependencies for kotlinx-io and Okio + +To use the extension functions with `kotlinx-io` or Okio types, add the dependencies you need. + +#### Add dependencies for `kotlinx-io` {initial-collapse-state="collapsed" collapsible="true"} - + ```kotlin // build.gradle.kts dependencies { implementation("org.jetbrains.kotlinx:kotlinx-serialization-json-io:%serializationVersion%") - implementation("org.jetbrains.kotlinx:kotlinx-serialization-json-okio:%serializationVersion%") - implementation("org.jetbrains.kotlinx:kotlinx-io-core:%kotlinxIoVersion%") - implementation("com.squareup.okio:okio:%okioVersion%") } - ``` - + ```groovy // build.gradle dependencies { implementation "org.jetbrains.kotlinx:kotlinx-serialization-json-io:%serializationVersion%" - implementation "org.jetbrains.kotlinx:kotlinx-serialization-json-okio:%serializationVersion%" - implementation "org.jetbrains.kotlinx:kotlinx-io-core:%kotlinxIoVersion%" - implementation "com.squareup.okio:okio:%okioVersion%" } ``` - + ```xml @@ -157,13 +156,48 @@ dependencies { org.jetbrains.kotlinx - kotlinx-serialization-json-okio - %serializationVersion% + kotlinx-io-core + %kotlinxIoVersion% + +``` + + + +#### Add dependencies for Okio {initial-collapse-state="collapsed" collapsible="true"} + + + + +```kotlin +// build.gradle.kts +dependencies { + implementation("org.jetbrains.kotlinx:kotlinx-serialization-json-okio:%serializationVersion%") + implementation("com.squareup.okio:okio:%okioVersion%") +} +``` + + + + +```groovy +// build.gradle +dependencies { + implementation "org.jetbrains.kotlinx:kotlinx-serialization-json-okio:%serializationVersion%" + implementation "com.squareup.okio:okio:%okioVersion%" +} +``` + + + + +```xml + + org.jetbrains.kotlinx - kotlinx-io-core - %kotlinxIoVersion% + kotlinx-serialization-json-okio + %serializationVersion% com.squareup.okio @@ -175,10 +209,6 @@ dependencies { -The next sections cover examples using `kotlinx-io` types with these APIs. -You can use Okio types similarly with their corresponding `BufferedSource` and `BufferedSink`. - - ### Serialize JSON to Sinks Use the `.encodeToSink()` function to serialize JSON to a `Sink`: @@ -277,4 +307,47 @@ fun main() { > You can iterate through sequences returned by `.decodeSourceToSequence()` only once > because they are tied to the underlying `Source`. > +> Closing the stream before the sequence is fully evaluated causes an `IOException`. +> {style="note"} + +## Okio + +```kotlin +// Imports declarations from the serialization library +import kotlinx.serialization.* +import kotlinx.serialization.json.* + +// Imports declarations for Okio types and JSON Okio support +import kotlinx.serialization.json.okio.* +import okio.BufferedSource +import okio.FileSystem +import okio.Path.Companion.toPath +import okio.buffer + + +@Serializable +data class Project(val name: String, val stars: Int) + +@OptIn(ExperimentalSerializationApi::class) +fun main() { + // Opens a BufferedSource for the "projects.json" file containing multiple JSON objects + val path = "projects.json".toPath() + FileSystem.SYSTEM.source(path).buffer().use { source: BufferedSource -> + + // Lazily deserializes each Project as it is read from the BufferedSource + val projects: Sequence = Json.decodeBufferedSourceToSequence(source) + + for (project in projects) { + println(project) + } + } +} + +``` + + +## What's next + +* Explore [advanced JSON element handling](serialization-json-elements.md) to manipulate and work with JSON data before it's parsed or serialized. +* Discover how to [transform JSON during serialization and deserialization](serialization-transform-json.md) for more control over your data.