Spec-Zone.ru › Kotlin 2

Настройка экземпляра Json

Экземпляр Json по умолчанию строго соответствует спецификации JSON и объявлениям в классах Kotlin.

Вы можете использовать более гибкие возможности JSON или преобразования типов, создав пользовательский экземпляр Json с помощью функции-построителя Json():

// Creates a Json instance based on the default configuration, allowing special floating-point values
val customJson = Json {
    allowSpecialFloatingPointValues = true
}

// Use the customJson instance with the same syntax as the default one to encode a string
val jsonString = customJson.encodeToString(Data(Double.NaN))
println(jsonString)

Экземпляры Json, созданные таким способом, неизменяемы и потокобезопасны, поэтому их можно безопасно хранить в свойстве верхнего уровня и использовать повторно.

Повторное использование пользовательских экземпляров Json повышает производительность, позволяя им кэшировать информацию, относящуюся к конкретным классам.

Также можно создать новый экземпляр Json на основе существующего и изменить его настройки с помощью того же синтаксиса построителя:

// Creates a new instance based on an existing Json
val lenientJson = Json(customJson) {
    isLenient = true
    prettyPrint = true
}

Настройка структуры JSON

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

Кодирование значений по умолчанию

По умолчанию кодировщик JSON не включает значения свойств по умолчанию, поскольку при декодировании они автоматически применяются к отсутствующим свойствам. Такое поведение особенно полезно для свойств, допускающих значение null и имеющих значение null по умолчанию, поскольку позволяет не записывать ненужные значения null. Подробнее см. раздел Управление сериализацией свойств со значениями по умолчанию.

Чтобы изменить это поведение по умолчанию, задайте свойству encodeDefaults значение true в экземпляре Json:

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

Исключение явных значений null

По умолчанию все значения null кодируются в выходные данные JSON. Чтобы исключить значения null, задайте свойству explicitNulls значение false в экземпляре Json:

// Imports declarations from the serialization library
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<Project>(json))
    // Project(name=kotlinx.serialization, language=Kotlin, version=1.2.2, website=null, description=null)
}
//sampleEnd

Если для explicitNulls задано значение false, кодирование и декодирование могут стать асимметричными. В этом примере свойство version имеет значение null перед кодированием, но при декодировании получает значение 1.2.2.

Можно настроить декодер так, чтобы он обрабатывал некоторые недопустимые входные значения, считая их отсутствующими свойствами. Для этого используется свойство coerceInputValues. Подробнее см. раздел Приведение входных значений.

Разрешение структурированных ключей карт

Формат JSON изначально не поддерживает карты со структурированными ключами, поскольку ключи JSON — это строки, представляющие только примитивы или перечисления. Чтобы сериализовать и десериализовать карты с ключами, заданными пользовательскими классами, используйте свойство allowStructuredMapKeys:

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

Разрешение специальных значений с плавающей точкой

По умолчанию специальные значения с плавающей точкой, такие как Double.NaN и бесконечности, не поддерживаются в JSON, поскольку спецификация JSON запрещает их.

Чтобы включить их кодирование и декодирование, задайте свойству allowSpecialFloatingPointValues значение true в экземпляре Json:

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

Задание дискриминатора класса для полиморфизма

При работе с полиморфными данными можно использовать свойство classDiscriminator, чтобы задать имя ключа, идентифицирующего тип сериализуемого полиморфного объекта. В сочетании с явным сериализуемым именем, заданным аннотацией @SerialName, этот подход позволяет полностью контролировать итоговую структуру JSON:

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

Свойство classDiscriminator экземпляра Json позволяет задать один ключ-дискриминатор для всех полиморфных типов, а экспериментальная аннотация @JsonClassDiscriminator предоставляет больше гибкости. Она позволяет задать пользовательский дискриминатор непосредственно в базовом классе, откуда он автоматически наследуется всеми подклассами.

Подробнее о наследуемых аннотациях сериализации см. в документации @InheritableSerialInfo.

Пример:

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

Нельзя задавать разные дискриминаторы классов в подклассах запечатанного базового класса. Собственные дискриминаторы могут определять только иерархии с различными, не пересекающимися подклассами.

Если дискриминатор задан в обоих местах, @JsonClassDiscriminator имеет приоритет над значением в конфигурации Json.

Настройка режима вывода дискриминатора класса

Используйте свойство JsonBuilder.classDiscriminatorMode, чтобы управлять добавлением дискриминаторов классов в выходные данные JSON. По умолчанию дискриминатор добавляется только для полиморфных типов, что полезно при работе с иерархиями полиморфных классов.

Чтобы изменить это поведение, задайте свойству ClassDiscriminatorMode одно из следующих значений:

  • POLYMORPHIC: (по умолчанию) добавляет дискриминатор класса только для полиморфных типов.

  • ALL_JSON_OBJECTS: добавляет дискриминатор класса ко всем объектам JSON, где это возможно.

  • NONE: полностью исключает дискриминатор класса.

Пример со свойством ClassDiscriminatorMode, которому задано значение NONE:

// Imports declarations from the serialization library
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 не сможет десериализовать выходные данные в соответствующий тип.

Форматирование

По умолчанию Json формирует компактный вывод в одну строку.

Для повышения удобочитаемости можно добавить отступы и переносы строк, включив форматирование выходных данных. Для этого задайте свойству prettyPrint значение true в экземпляре Json:

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

Этот пример выводит следующий результат:

{
    "name": "kotlinx.serialization",
    "language": "Kotlin"
}

Для настройки отступов в форматированном JSON можно использовать параметр prettyPrintIndent.

Например, вместо четырёх пробелов по умолчанию можно использовать любые допустимые пробельные символы, например \t или \n.

Настройка десериализации JSON

Парсер Json в Kotlin предоставляет несколько параметров, позволяющих настроить разбор и десериализацию данных JSON.

Игнорирование неизвестных ключей

При работе с данными JSON из сторонних сервисов или других динамических источников со временем в объекты JSON могут добавляться новые свойства.

По умолчанию неизвестные ключи (имена свойств во входных данных JSON) приводят к ошибке десериализации. Чтобы этого избежать, задайте свойству ignoreUnknownKeys значение true в экземпляре Json:

// 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<Project>("""
        {"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

Игнорирование неизвестных ключей для определённых классов

Вместо того чтобы включать ignoreUnknownKeys для всех классов, можно игнорировать неизвестные ключи только для определённых классов с помощью аннотации @JsonIgnoreUnknownKeys. Это позволяет по умолчанию сохранять строгую десериализацию и допускать более свободную обработку только там, где это необходимо.

Аннотация @JsonIgnoreUnknownKeys является экспериментальной. Чтобы разрешить её использование, примените аннотацию @OptIn(ExperimentalSerializationApi::class) или параметр компилятора -opt-in=kotlinx.serialization.ExperimentalSerializationApi:

// 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<Outer>(
        """{"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<Outer>(
            """{"a":1,"inner":{"x":"value","unknownKey":"unexpected"}}"""
        )
    )
}
//sampleEnd

В этом примере Inner выбрасывает SerializationException при наличии неизвестных ключей, поскольку он не аннотирован с помощью @JsonIgnoreUnknownKeys.

Приведение входных значений

При работе с данными JSON из сторонних сервисов или других динамических источников формат может изменяться. Это может приводить к исключениям при декодировании, если фактические значения не соответствуют ожидаемым типам.

Реализация Json по умолчанию строго проверяет типы входных данных. Чтобы ослабить это ограничение, задайте свойству coerceInputValues значение true в экземпляре Json:

// 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<Project>("""
        {"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

Свойство coerceInputValues влияет только на декодирование. Оно обрабатывает некоторые недопустимые входные значения так, как если бы соответствующее свойство отсутствовало. В настоящее время это относится к следующим значениям:

  • null для типов, не допускающих null

  • неизвестные значения перечислений

В будущих версиях этот список может быть расширен, что сделает экземпляры Json с этим свойством ещё более терпимыми: недопустимые значения будут заменяться значениями по умолчанию или null.

Если значение отсутствует, оно заменяется значением свойства по умолчанию, если оно задано.

Для перечислений значение заменяется на null, только если:

  • Значение по умолчанию не задано.

  • Для свойства explicitNulls задано значение false.

  • Свойство допускает значение null.

Можно объединить coerceInputValues со свойством explicitNulls для обработки недопустимых значений перечислений:

// 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<Brush>("""{"foreground":"pink", "background":"purple"}""")
    println(brush)
    // Brush(foreground=BLACK, background=null)
}
//sampleEnd

Разрешение завершающих запятых

Чтобы разрешить завершающие запятые во входных данных JSON, задайте свойству allowTrailingComma значение true:

// 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<List<Int>>(
        """
            [1, 2, 3,]
        """
    )
    println(numbers)
    // [1, 2, 3]
}
//sampleEnd

Разрешение комментариев в JSON

Используйте свойство allowComments, чтобы разрешить комментарии во входных данных JSON. Если это свойство включено, парсер принимает следующие формы комментариев во входных данных:

  • // однострочные комментарии, заканчивающиеся символом новой строки \n

  • /* */ блочные комментарии

Пример:

// 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<List<Int>>(
        """
            [
                // first element
                1,
                /* second element */
                2
            ]
        """
    )
    println(numbers)
    // [1, 2]
}
//sampleEnd

Нестрогий разбор

По умолчанию парсер Json соблюдает строгие правила JSON, обеспечивая соответствие спецификации RFC-8259, согласно которой ключи и строковые литералы должны быть заключены в кавычки.

Чтобы ослабить эти ограничения, задайте свойству isLenient значение true в экземпляре Json:

// 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<Project>("""
        {
            name   : kotlinx.serialization,
            status : SUPPORTED,
            votes  : "9000"
        }
    """)
    println(data)
    // Project(name=kotlinx.serialization, status=SUPPORTED, votes=9000)
}
//sampleEnd

Настройка сопоставления имён между JSON и Kotlin

Некоторые данные JSON могут не полностью соответствовать соглашениям об именовании или ожидаемым форматам в Kotlin. Для решения этих задач библиотека сериализации Kotlin предоставляет несколько инструментов для управления расхождениями в именах, обработки нескольких имён свойств JSON и обеспечения единообразной стратегии именования сериализуемых данных.

Приём альтернативных имён свойств JSON для одного свойства Kotlin

Если имена свойств JSON меняются между версиями схемы, можно переименовать свойство JSON с помощью аннотации @SerialName.

Однако в этом случае невозможно декодировать данные, в которых всё ещё используются прежние имена свойств. Чтобы принимать альтернативные имена JSON для одного свойства, используйте аннотацию @JsonNames:

// Imports declarations from the serialization library
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<Project>("""{"name":"kotlinx.serialization"}""")
    println(project)
    // Project(name=kotlinx.serialization)

    val oldProject = Json.decodeFromString<Project>("""{"title":"kotlinx.coroutines"}""")
    // Both name and title Json properties correspond to name property
    println(oldProject)
    // Project(name=kotlinx.coroutines)
}
//sampleEnd

Свойство useAlternativeNames в JsonBuilder включает аннотацию @JsonNames. По умолчанию для этого свойства задано значение true, позволяющее Json распознавать и декодировать несколько имён одного свойства.

Если вы не используете @JsonNames и хотите повысить производительность, особенно при пропуске множества неизвестных свойств с помощью ignoreUnknownKeys, этому свойству можно задать значение false.

Регистронезависимое декодирование перечислений

Соглашения об именовании в Kotlin рекомендуют задавать имена значений перечислений прописными буквами с подчёркиваниями или в верхнем верблюжьем регистре. По умолчанию Json при декодировании использует точные имена констант перечислений Kotlin.

Однако в данных JSON из внешних источников могут использоваться имена в нижнем или смешанном регистре. Чтобы обрабатывать такие случаи, настройте экземпляр Json для регистронезависимого декодирования значений перечислений с помощью свойства JsonBuilder.decodeEnumsCaseInsensitive:

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

fun main() {
    // Decodes enum values regardless of their case, including alternative names
    println(format.decodeFromString<CasesList>("""{"cases":["value_A", "alternative"]}""")) 
    // CasesList(cases=[VALUE_A, VALUE_B])
}
//sampleEnd

Это свойство применяется как к сериализуемым именам, так и к альтернативным именам, заданным аннотацией @JsonNames, и гарантирует успешное декодирование всех значений. Это свойство не влияет на кодирование.

Применение глобальной стратегии именования

Если имена свойств во входных данных JSON отличаются от имён в Kotlin, можно явно задать имя каждого свойства с помощью аннотации @SerialName. Однако при переходе с других фреймворков или устаревшей кодовой базы может потребоваться одинаково преобразовать каждое сериализуемое имя.

В таких случаях можно задать глобальную стратегию именования с помощью свойства JsonBuilder.namingStrategy в экземпляре Json. Библиотека сериализации Kotlin предоставляет встроенные стратегии, например JsonNamingStrategy.SnakeCase:

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

При использовании глобальной стратегии именования с JsonNamingStrategy учитывайте следующее:

  • Преобразование применяется ко всем свойствам, независимо от того, выведено ли сериализуемое имя из имени свойства или задано явно аннотацией @SerialName. Нельзя исключить свойство из преобразования, задав сериализуемое имя. Чтобы сохранить без изменений определённые имена при сериализации, используйте вместо этого аннотацию @JsonNames.

  • Если преобразованное имя конфликтует с другими преобразованными именами свойств или с альтернативными именами, заданными аннотацией @JsonNames, десериализация завершится с исключением.

  • Глобальные стратегии именования применяются неявно. Из-за этого по определению класса сложно определить сериализованные имена. Это может затруднить такие задачи, как Поиск использований и Переименование в IDE, а также полнотекстовый поиск с помощью таких инструментов, как grep, что потенциально повышает риск ошибок и затраты на сопровождение.

Учитывая эти факторы, тщательно взвесьте все компромиссы перед внедрением глобальных стратегий именования в приложении.

Что дальше

  • Изучите расширенную обработку элементов JSON, чтобы изменять данные JSON и работать с ними до их разбора или сериализации.

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

15 января 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-configuration.html

Spec-Zone.ru

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