Kotlin Help

JSON elements

The Kotlin serialization library also supports working with JSON at a structural level. You can use the JsonElement API, to inspect, modify, and build JSON structures directly before converting them into a Kotlin type or a string.

JsonElement has three direct subtypes that represent the core JSON structures:

  • JsonPrimitive handles primitive JSON elements such as strings, numbers, booleans, and null. The null value is represented by a special subclass of JsonPrimitive, called JsonNull. Each JsonPrimitive stores a string representation of its value, which you can access through its JsonPrimitive.content property.

  • JsonArray represents a JSON array. It's a Kotlin List of JsonElement items.

  • JsonObject represents a JSON object. It's a Kotlin Map with String keys and JsonElement values.

Parse strings to JSON elements

You can parse a string into a JsonElement to work with the JSON structure before converting it into a Kotlin type or a string.

Use the Json.parseToJsonElement() function to parse the input into a JSON element tree without decoding or deserializing it:

// Imports declarations from the serialization library import kotlinx.serialization.* import kotlinx.serialization.json.* //sampleStart fun main() { val element = Json.parseToJsonElement(""" {"name":"kotlinx.serialization","language":"Kotlin"} """) // JsonElement.toString() gives you a valid JSON string println(element) // {"name":"kotlinx.serialization","language":"Kotlin"} } //sampleEnd

Access the contents of JSON elements

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:

Similarly, JsonPrimitive has extension properties for parsing the value as Kotlin primitive types, such as int, intOrNull, long, and longOrNull.

Here's an example of how you can use these extension properties when processing JSON data with a known structure:

// Imports declarations from the serialization library 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

If you don't know the JSON structure in advance, you can check the element type and handle each JsonElement subtype explicitly. For example, you can use a helper function with a when expression:

fun processElement(element: JsonElement): String = when (element) { is JsonObject -> "JsonObject with keys: ${element.keys}" is JsonArray -> "JsonArray with ${element.size} elements" is JsonPrimitive -> "JsonPrimitive with content: ${element.content}" }

Create JSON elements

You can create instances of specific JsonElement subtypes directly.

To create a JsonPrimitive, use the JsonPrimitive() function:

// Imports declarations from the serialization library 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

You can create JsonArray and JsonObject elements either by directly calling their constructors or by using the builder functions:

The builder functions provide a DSL similar to Kotlin's standard library collection builders with JSON-specific overloads and inner builder functions.

Here's an example that highlights the key features of the JSON builder DSLs:

// Imports declarations from the serialization library 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

Encode literal JSON content

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 for large numbers, the value might get truncated and you lose precision. If you use Kotlin/JVM BigDecimal, the value stays precise, but JsonPrimitive() encodes the value as a string rather than as a number:

// Imports declarations from the serialization library 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

In this example, even though pi is defined as a number with 30 decimal places, the resulting JSON doesn't preserve that precision. The Double value is truncated to 15 decimal places, and the String value is wrapped in quotes, so it becomes a JSON string rather than a number.

To avoid these issues, you can encode an arbitrary unquoted value, such as the string value of pi in this example, using the JsonUnquotedLiteral() function:

// Imports declarations from the serialization library 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

To decode pi back to a BigDecimal, extract the string content of the JsonPrimitive:

// Imports declarations from the serialization library 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

JSON null literal

To avoid creating an inconsistent state, you can't encode the string "null" with the JsonUnquotedLiteral() function. If you try to do so, an exception is thrown:

// Imports declarations from the serialization library 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

To represent a JSON null literal value, use JsonNull:

// Imports declarations from the serialization library import kotlinx.serialization.* import kotlinx.serialization.json.* //sampleStart fun main() { val possiblyNull = JsonNull println(possiblyNull) // null } //sampleEnd

Decode Json elements

To decode an instance of the JsonElement class into a serializable object, use the Json.decodeFromJsonElement() function:

// Imports declarations from the serialization library 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

What's next

01 April 2026