Spec-Zone.ru › Kotlin 2

JSON-элементы

Библиотека сериализации Kotlin также поддерживает работу с JSON на структурном уровне. Вы можете использовать API JsonElement, чтобы изучать, изменять и создавать структуры JSON напрямую, прежде чем преобразовать их в тип Kotlin или строку.

JsonElement имеет три непосредственных подтипа, представляющих основные структуры JSON:

  • JsonPrimitive обрабатывает примитивные элементы JSON, такие как строки, числа, логические значения и null. Значение null представлено специальным подклассом JsonPrimitive под названием JsonNull. Каждый JsonPrimitive хранит строковое представление своего значения, доступное через свойство JsonPrimitive.content.

  • JsonArray представляет массив JSON. Это Kotlin-список List, состоящий из элементов JsonElement.

  • JsonObject представляет объект JSON. Это Kotlin-словарь Map с ключами String и значениями JsonElement.

Преобразование строк в JSON-элементы

Вы можете преобразовать строку в JsonElement, чтобы работать со структурой JSON до преобразования в тип Kotlin или строку.

Используйте функцию Json.parseToJsonElement(), чтобы преобразовать входные данные в дерево элементов JSON без декодирования или десериализации:

// 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

Доступ к содержимому JSON-элементов

Вы можете получить доступ к содержимому элемента JSON напрямую через свойства-расширения API JsonElement. Эти свойства-расширения преобразуют элемент в определённый подтип и выбрасывают IllegalArgumentException, если структура JSON элемента не соответствует ожидаемой.

Доступны следующие свойства-расширения:

  • jsonPrimitive возвращает JsonPrimitive.

  • jsonArray возвращает JsonArray.

  • jsonObject возвращает JsonObject.

Аналогичным образом, у JsonPrimitive есть свойства-расширения для разбора значения в примитивные типы Kotlin, такие как int, intOrNull, long и longOrNull.

Вот пример использования этих свойств-расширений при обработке данных JSON с известной структурой:

// 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

Если структура JSON заранее неизвестна, можно проверить тип элемента и обработать каждый подтип JsonElement явно. Например, можно использовать вспомогательную функцию с выражением when:

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}"
}

Создание JSON-элементов

Вы можете напрямую создавать экземпляры определённых подтипов JsonElement.

Чтобы создать JsonPrimitive, используйте функцию JsonPrimitive():

// 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

Элементы JsonArray и JsonObject можно создавать, напрямую вызывая их конструкторы или используя функции-билдеры:

  • Чтобы создать JsonArray из List, используйте JsonArray() или функцию-билдер buildJsonArray().

  • Чтобы создать JsonObject из Map, используйте JsonObject() или функцию-билдер buildJsonObject().

Функции-билдеры предоставляют DSL, похожий на функции-билдеры коллекций стандартной библиотеки Kotlin, с перегрузками для JSON и внутренними функциями-билдерами.

Вот пример, демонстрирующий основные возможности DSL-билдеров JSON:

// 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

Кодирование литерального содержимого JSON

Хотя спецификация JSON не ограничивает размер или точность чисел, сериализация чисел произвольного размера с помощью функции JsonPrimitive() может привести к некоторым проблемам.

Например, если использовать Double для больших чисел, значение может быть усечено, и точность будет потеряна. Если использовать Kotlin/JVM BigDecimal, значение сохранит точность, но JsonPrimitive() закодирует его как строку, а не как число:

// 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

В этом примере, хотя pi определено как число с 30 знаками после запятой, итоговый JSON не сохраняет эту точность. Значение Double усечено до 15 знаков после запятой, а значение String заключено в кавычки и поэтому становится строкой JSON, а не числом.

Чтобы избежать этих проблем, можно закодировать произвольное значение без кавычек, например строковое значение pi в этом примере, с помощью функции JsonUnquotedLiteral():

// 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

Чтобы декодировать pi обратно в BigDecimal, извлеките строковое содержимое из 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

Для простоты в этом примере используется JsonPrimitive. Более универсальные подходы см. в разделе Преобразования JSON.

Литерал JSON null

Чтобы избежать создания некорректного состояния, нельзя кодировать строку "null" с помощью функции JsonUnquotedLiteral(). При попытке это сделать будет выброшено исключение:

// 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

Чтобы представить литеральное значение JSON null, используйте JsonNull:

// Imports declarations from the serialization library
import kotlinx.serialization.*
import kotlinx.serialization.json.*

//sampleStart
fun main() {
    val possiblyNull = JsonNull
  
    println(possiblyNull)
    // null
}
//sampleEnd

Декодирование элементов JSON

Чтобы декодировать экземпляр класса JsonElement в сериализуемый объект, используйте функцию Json.decodeFromJsonElement():

// 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

Что дальше

  • Узнайте, как преобразовывать JSON при сериализации и десериализации, чтобы лучше контролировать данные.

  • Узнайте, как сериализовать классы и изменять поведение аннотации @Serializable по умолчанию.

1 апреля 2026 г.
Настройка экземпляра JsonПреобразование структуры JSON

© 2010–2026 JetBrains s.r.o. and Kotlin Programming Language contributors
Licensed under the Apache License, Version 2.0.
https://kotlinlang.org/docs/serialization-json-elements.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API