-
Notifications
You must be signed in to change notification settings - Fork 670
feat: adding JSON elements page #3121
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: doc-restructuring-master
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| <?xml version="1.0" encoding="UTF-8"?> | ||
| <!DOCTYPE labels SYSTEM "https://resources.jetbrains.com/writerside/1.0/labels-list.dtd"> | ||
| <labels xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| xsi:noNamespaceSchemaLocation="https://resources.jetbrains.com/writerside/1.0/labels.xsd"> | ||
|
|
||
| <primary-label id="experimental-general" name="Experimental" short-name="Experimental" href="components-stability.html#stability-levels-explained" color="red">The feature is Experimental. It may be dropped or changed at any time. Use it only for evaluation purposes.</primary-label> | ||
| <primary-label id="experimental-opt-in" name="Experimental" short-name="Experimental" href="components-stability.html#stability-levels-explained" color="red" >The feature is Experimental. It may be dropped or changed at any time. Opt-in is required (see the details below), and you should use it only for evaluation purposes.</primary-label> | ||
| <primary-label id="alpha" name="Alpha" short-name="α" href="components-stability.html#stability-levels-explained" color="strawberry">The feature is in Alpha. It may change incompatibly and require manual migration in the future.</primary-label> | ||
| <primary-label id="beta" name="Beta" short-name="β" href="components-stability.html#stability-levels-explained" color="tangerine">The feature is in Beta. It is almost stable, but migration steps may be required in the future. We'll do our best to minimize any changes you have to make.</primary-label> | ||
| <primary-label id="advanced" name="Advanced" short-name="☆" color="purple"></primary-label> | ||
| <primary-label id="eap" name="EAP" short-name="EAP" color="red">This functionality is available only in the latest EAP version.</primary-label> | ||
|
|
||
| <secondary-label id="eap" name="EAP" color="red">This functionality is available only in the latest EAP version.</secondary-label> | ||
| </labels> | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,341 @@ | ||
| [//]: # (title: JSON elements) | ||
|
|
||
| Besides converting between JSON strings and Kotlin objects, the Kotlin serialization library also supports working with JSON at a structural level. | ||
| To do this, you can use the [`JsonElement`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-element/) API, which lets you inspect, modify, and construct JSON structure directly before converting it into a Kotlin type. | ||
sandwwraith marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| `JsonElement` has three direct subtypes that represent the core JSON structures: | ||
|
|
||
| * [`JsonPrimitive`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-primitive/) handles primitive JSON elements such as strings, numbers, booleans, and `null`. | ||
sandwwraith marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| Each `JsonPrimitive` stores a string representation of its value, which you can access through its [`JsonPrimitive.content`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-primitive/content.html) property. | ||
| * [`JsonArray`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-array/) is a JSON array. It's a Kotlin [`List`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/) of `JsonElement` items. | ||
| * [`JsonObject`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-object/) is a JSON object. It's a Kotlin [`Map`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-map/#kotlin.collections.Map) with `String` keys and `JsonElement` values. | ||
|
|
||
| > You can create a `JsonPrimitive` by passing primitive Kotlin types to the [`JsonPrimitive()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-primitive.html) function. | ||
sandwwraith marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| > | ||
| {style="note"} | ||
|
|
||
| ## Parse to JSON elements | ||
|
|
||
| You can parse a string into a `JsonElement` to work with the JSON structure before converting it into a Kotlin type. | ||
| To do so, use the [`Json.parseToJsonElement()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/parse-to-json-element.html) function. | ||
| This function parses the input into a JSON element tree without decoding or deserializing it. | ||
|
|
||
| Here's an example: | ||
|
|
||
| ```kotlin | ||
| // Imports declarations from the serialization and JSON handling libraries | ||
sandwwraith marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| import kotlinx.serialization.* | ||
| import kotlinx.serialization.json.* | ||
|
|
||
| //sampleStart | ||
| fun main() { | ||
| val element = Json.parseToJsonElement(""" | ||
| {"name":"kotlinx.serialization","language":"Kotlin"} | ||
| """) | ||
| // Prints the JsonElement as a valid JSON string | ||
sandwwraith marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| println(element) | ||
| // {"name":"kotlinx.serialization","language":"Kotlin"} | ||
| } | ||
| //sampleEnd | ||
| ``` | ||
| {kotlin-runnable="true"} | ||
|
|
||
| ## Access JSON element contents | ||
|
|
||
| You can access the contents of a JSON element directly through the extension properties of the `JsonElement` API. | ||
| These extension properties cast the element to a specific subtype, | ||
| and throw an `IllegalArgumentException` if the element doesn't have the expected JSON structure. | ||
|
|
||
| The available extension properties are: | ||
|
|
||
| * [`jsonPrimitive`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/json-primitive.html) returns a `JsonPrimitive`. | ||
| * [`jsonArray`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/json-array.html) returns a `JsonArray`. | ||
| * [`jsonObject`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/json-object.html) returns a `JsonObject`. | ||
sandwwraith marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| Similarly, [`JsonPrimitive`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-primitive/) has extension properties for parsing the value as Kotlin primitive types, such as | ||
| [`int`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/int.html), [`intOrNull`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/int-or-null.html), [`long`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/long.html), [`longOrNull`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/long-or-null.html), and so on. | ||
|
|
||
| Here's an example of how you can use these extension properties when processing JSON data: | ||
|
||
|
|
||
| ```kotlin | ||
| // Imports declarations from the serialization and JSON handling libraries | ||
| import kotlinx.serialization.* | ||
| import kotlinx.serialization.json.* | ||
|
|
||
| //sampleStart | ||
| fun main() { | ||
| val element = Json.parseToJsonElement(""" | ||
| { | ||
| "name": "kotlinx.serialization", | ||
| "forks": [{"votes": 42}, {"votes": 9000}, {}] | ||
| } | ||
| """) | ||
| val sum = element | ||
| // Accesses the forks key from the JsonObject | ||
| .jsonObject["forks"]!! | ||
|
|
||
| // Accesses the value as a JsonArray and sums the votes values from each JsonObject as Int | ||
| .jsonArray.sumOf { it.jsonObject["votes"]?.jsonPrimitive?.int ?: 0 } | ||
| println(sum) | ||
| // 9042 | ||
| } | ||
| //sampleEnd | ||
| ``` | ||
| {kotlin-runnable="true"} | ||
|
|
||
| ## Create JSON elements | ||
|
|
||
| You can create instances of specific `JsonElement` subtypes directly. | ||
|
|
||
| To create a `JsonPrimitive`, use the `JsonPrimitive()` function: | ||
|
|
||
| ```kotlin | ||
| // Imports declarations from the serialization and JSON handling libraries | ||
| import kotlinx.serialization.* | ||
| import kotlinx.serialization.json.* | ||
|
|
||
| //sampleStart | ||
| fun main() { | ||
| // Creates JsonPrimitive values from different Kotlin primitives | ||
| val number = JsonPrimitive(42) | ||
| val text = JsonPrimitive("kotlinx.serialization") | ||
|
|
||
| println(number) | ||
| // 42 | ||
| println(text) | ||
| // "kotlinx.serialization" | ||
| } | ||
| //sampleEnd | ||
| ``` | ||
| {kotlin-runnable="true"} | ||
|
|
||
| To create `JsonArray` or `JsonObject` elements, use the | ||
sandwwraith marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| [`buildJsonArray()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/build-json-array.html) and [`buildJsonObject()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/build-json-object.html) builder functions respectively. | ||
| These provide a DSL to define the JSON structure, similar to [Kotlin's standard library collection builders](constructing-collections.md#create-with-collection-builder-functions), but with JSON-specific overloads and inner builder functions. | ||
|
|
||
| Let's look at an example that highlights the key features: | ||
|
|
||
| ```kotlin | ||
| // Imports declarations from the serialization and JSON handling libraries | ||
| import kotlinx.serialization.* | ||
| import kotlinx.serialization.json.* | ||
|
|
||
| //sampleStart | ||
| fun main() { | ||
| val element = buildJsonObject { | ||
| // Adds a simple key-value pair to the JsonObject | ||
| put("name", "kotlinx.serialization") | ||
| // Adds a nested JsonObject under the owner key | ||
| putJsonObject("owner") { | ||
| put("name", "kotlin") | ||
| } | ||
| // Adds a JsonArray with multiple JsonObjects | ||
| putJsonArray("forks") { | ||
| // Adds a JsonObject to the JsonArray | ||
| addJsonObject { | ||
| put("votes", 42) | ||
| } | ||
| addJsonObject { | ||
| put("votes", 9000) | ||
| } | ||
| } | ||
| } | ||
| // Prints the resulting JSON string | ||
| println(element) | ||
| // {"name":"kotlinx.serialization","owner":{"name":"kotlin"},"forks":[{"votes":42},{"votes":9000}]} | ||
| } | ||
| //sampleEnd | ||
| ``` | ||
| {kotlin-runnable="true"} | ||
|
|
||
| ### Encode literal JSON content | ||
| <primary-label ref="experimental-general"/> | ||
sandwwraith marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| While the JSON specification doesn't restrict the size or precision of numbers, serializing numbers of arbitrary size with the `JsonPrimitive()` function might lead to some issues. | ||
|
|
||
| For example, if you use [`Double`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-double/) for large numbers, the value might get truncated and you lose precision. | ||
| If you use Kotlin/JVM [`BigDecimal`](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/java.math.-big-decimal/), the value stays precise, but `JsonPrimitive()` encodes the value as a string rather than as a number: | ||
|
|
||
| ```kotlin | ||
| // Imports necessary declarations from libraries | ||
| import kotlinx.serialization.* | ||
| import kotlinx.serialization.json.* | ||
| import java.math.BigDecimal | ||
|
|
||
| //sampleStart | ||
| val format = Json { prettyPrint = true } | ||
|
|
||
| fun main() { | ||
| val pi = BigDecimal("3.141592653589793238462643383279") | ||
|
|
||
| // Converts the BigDecimal to a Double, causing potential truncation | ||
| val piJsonDouble = JsonPrimitive(pi.toDouble()) | ||
| // Converts the BigDecimal to a String, preserving the precision but treating it as a string in JSON | ||
| val piJsonString = JsonPrimitive(pi.toString()) | ||
|
|
||
| val piObject = buildJsonObject { | ||
| put("pi_double", piJsonDouble) | ||
| put("pi_string", piJsonString) | ||
| } | ||
|
|
||
| println(format.encodeToString(piObject)) | ||
| // "pi_double": 3.141592653589793, | ||
| // "pi_string": "3.141592653589793238462643383279" | ||
| } | ||
| //sampleEnd | ||
| ``` | ||
| {kotlin-runnable="true"} | ||
|
|
||
| In this example, even though `pi` is defined as a number with 30 decimal places, the resulting JSON doesn't reflect this. | ||
| The `Double` value is truncated to 15 decimal places, and the `String` is wrapped in quotes, making it a string instead of a JSON number. | ||
|
|
||
| > The [`JsonUnquotedLiteral()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-unquoted-literal.html) function 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"} | ||
|
|
||
| To avoid these issues, you can encode an arbitrary unquoted value, such as the string value of `pi` in this example, using the [`JsonUnquotedLiteral()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-unquoted-literal.html) function: | ||
|
|
||
| ```kotlin | ||
| // Imports necessary declarations from libraries | ||
| import kotlinx.serialization.* | ||
| import kotlinx.serialization.json.* | ||
| import java.math.BigDecimal | ||
|
|
||
| //sampleStart | ||
| val format = Json { prettyPrint = true } | ||
|
|
||
| fun main() { | ||
| val pi = BigDecimal("3.141592653589793238462643383279") | ||
|
|
||
| // Encodes the raw JSON content using JsonUnquotedLiteral | ||
| @OptIn(ExperimentalSerializationApi::class) | ||
| val piJsonLiteral = JsonUnquotedLiteral(pi.toString()) | ||
|
|
||
| // Converts to Double and String | ||
| val piJsonDouble = JsonPrimitive(pi.toDouble()) | ||
| val piJsonString = JsonPrimitive(pi.toString()) | ||
|
|
||
| val piObject = buildJsonObject { | ||
| put("pi_literal", piJsonLiteral) | ||
| put("pi_double", piJsonDouble) | ||
| put("pi_string", piJsonString) | ||
| } | ||
|
|
||
| // pi_literal now accurately matches the value defined. | ||
| println(format.encodeToString(piObject)) | ||
| // "pi_literal": 3.141592653589793238462643383279, | ||
| // "pi_double": 3.141592653589793, | ||
| // "pi_string": "3.141592653589793238462643383279" | ||
| } | ||
| //sampleEnd | ||
| ``` | ||
| {kotlin-runnable="true"} | ||
|
|
||
| To decode `pi` back to a `BigDecimal`, extract the string content of the `JsonPrimitive`: | ||
|
|
||
| ```kotlin | ||
| // Imports necessary declarations from libraries | ||
| import kotlinx.serialization.* | ||
| import kotlinx.serialization.json.* | ||
| import java.math.BigDecimal | ||
|
|
||
| //sampleStart | ||
| fun main() { | ||
| val piObjectJson = """ | ||
| { | ||
| "pi_literal": 3.141592653589793238462643383279 | ||
| } | ||
| """.trimIndent() | ||
|
|
||
| // Decodes the JSON string into a JsonObject | ||
| val piObject: JsonObject = Json.decodeFromString(piObjectJson) | ||
|
|
||
| // Extracts the string content from the JsonPrimitive | ||
| val piJsonLiteral = piObject["pi_literal"]!!.jsonPrimitive.content | ||
|
|
||
| // Converts the string to a BigDecimal | ||
| val pi = BigDecimal(piJsonLiteral) | ||
| // Prints the decoded value of pi, preserving all 30 decimal places | ||
| println(pi) | ||
| // 3.141592653589793238462643383279 | ||
| } | ||
| //sampleEnd | ||
| ``` | ||
| {kotlin-runnable="true"} | ||
|
|
||
| > This example uses a `JsonPrimitive` for simplicity. For more reusable approaches, see | ||
| > [Json Transformations](serialization-transform-json.md). | ||
| > | ||
| {style="tip"} | ||
|
|
||
| #### JSON null literal | ||
|
|
||
| To avoid creating an inconsistent state, you can't encode the string `"null"` with `JsonUnquotedLiteral`. | ||
| Attempting to do so results in an exception: | ||
|
|
||
| ```kotlin | ||
| // Imports the necessary libraries | ||
| import kotlinx.serialization.* | ||
| import kotlinx.serialization.json.* | ||
|
|
||
| //sampleStart | ||
| @OptIn(ExperimentalSerializationApi::class) | ||
| fun main() { | ||
| JsonUnquotedLiteral("null") | ||
| // Exception in thread "main" kotlinx.serialization.json.internal.JsonEncodingException | ||
| } | ||
| //sampleEnd | ||
| ``` | ||
| {kotlin-runnable="true" validate="false"} | ||
|
|
||
| To represent a JSON `null` literal value, use [`JsonNull`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-null/) instead: | ||
|
|
||
| ```kotlin | ||
| // Imports declarations from the serialization and JSON handling libraries | ||
| import kotlinx.serialization.* | ||
| import kotlinx.serialization.json.* | ||
|
|
||
| //sampleStart | ||
| fun main() { | ||
| val possiblyNull = JsonNull | ||
|
|
||
| println(possiblyNull) | ||
| // null | ||
| } | ||
| //sampleEnd | ||
| ``` | ||
| {kotlin-runnable="true"} | ||
|
|
||
| ## Decode Json elements | ||
|
|
||
| To decode an instance of the `JsonElement` class into a serializable object, use | ||
| the [`Json.decodeFromJsonElement()`](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/decode-from-json-element.html) function: | ||
|
|
||
| ```kotlin | ||
| // Imports declarations from the serialization and JSON handling libraries | ||
| import kotlinx.serialization.* | ||
| import kotlinx.serialization.json.* | ||
|
|
||
| //sampleStart | ||
| @Serializable | ||
| data class Project(val name: String, val language: String) | ||
|
|
||
| fun main() { | ||
| val element = buildJsonObject { | ||
| put("name", "kotlinx.serialization") | ||
| put("language", "Kotlin") | ||
| } | ||
|
|
||
| // Decodes the JsonElement into a Project object | ||
| val data = Json.decodeFromJsonElement<Project>(element) | ||
| println(data) | ||
| // Project(name=kotlinx.serialization, language=Kotlin) | ||
| } | ||
| //sampleEnd | ||
| ``` | ||
| {kotlin-runnable="true"} | ||
|
|
||
| ## What's next | ||
|
|
||
| * Discover how to [transform JSON during serialization and deserialization](serialization-transform-json.md) for more control over your data. | ||
| * Learn how to [serialize classes](serialization-customization-options.md) and how to modify the default behavior of the `@Serializable` annotation. | ||
Uh oh!
There was an error while loading. Please reload this page.