| <!--- TEST_NAME JsonTest --> |
| |
| # JSON features |
| |
| This is the fifth chapter of the [Kotlin Serialization Guide](serialization-guide.md). |
| In this chapter, we'll walk through features of [JSON](https://www.json.org/json-en.html) serialization available in the [Json] class. |
| |
| **Table of contents** |
| |
| <!--- TOC --> |
| |
| * [Json configuration](#json-configuration) |
| * [Pretty printing](#pretty-printing) |
| * [Lenient parsing](#lenient-parsing) |
| * [Ignoring unknown keys](#ignoring-unknown-keys) |
| * [Alternative Json names](#alternative-json-names) |
| * [Coercing input values](#coercing-input-values) |
| * [Encoding defaults](#encoding-defaults) |
| * [Explicit nulls](#explicit-nulls) |
| * [Allowing structured map keys](#allowing-structured-map-keys) |
| * [Allowing special floating-point values](#allowing-special-floating-point-values) |
| * [Class discriminator for polymorphism](#class-discriminator-for-polymorphism) |
| * [Class discriminator output mode](#class-discriminator-output-mode) |
| * [Decoding enums in a case-insensitive manner](#decoding-enums-in-a-case-insensitive-manner) |
| * [Global naming strategy](#global-naming-strategy) |
| * [Json elements](#json-elements) |
| * [Parsing to Json element](#parsing-to-json-element) |
| * [Types of Json elements](#types-of-json-elements) |
| * [Json element builders](#json-element-builders) |
| * [Decoding Json elements](#decoding-json-elements) |
| * [Encoding literal Json content (experimental)](#encoding-literal-json-content-experimental) |
| * [Serializing large decimal numbers](#serializing-large-decimal-numbers) |
| * [Using `JsonUnquotedLiteral` to create a literal unquoted value of `null` is forbidden](#using-jsonunquotedliteral-to-create-a-literal-unquoted-value-of-null-is-forbidden) |
| * [Json transformations](#json-transformations) |
| * [Array wrapping](#array-wrapping) |
| * [Array unwrapping](#array-unwrapping) |
| * [Manipulating default values](#manipulating-default-values) |
| * [Content-based polymorphic deserialization](#content-based-polymorphic-deserialization) |
| * [Under the hood (experimental)](#under-the-hood-experimental) |
| * [Maintaining custom JSON attributes](#maintaining-custom-json-attributes) |
| |
| <!--- END --> |
| |
| ## Json configuration |
| |
| The default [Json] implementation is quite strict with respect to invalid inputs. It enforces Kotlin type safety and |
| restricts Kotlin values that can be serialized so that the resulting JSON representations are standard. |
| Many non-standard JSON features are supported by creating a custom instance of a JSON _format_. |
| |
| To use a custom JSON format configuration, create your own [Json] class instance from an existing |
| instance, such as a default `Json` object, using the [Json()] builder function. Specify parameter values |
| in the parentheses via the [JsonBuilder] DSL. The resulting `Json` format instance is immutable and thread-safe; |
| it can be simply stored in a top-level property. |
| |
| > We recommend that you store and reuse custom instances of formats for performance reasons because format implementations |
| > may cache format-specific additional information about the classes they serialize. |
| |
| This chapter shows configuration features that [Json] supports. |
| |
| <!--- INCLUDE .*-json-.* |
| import kotlinx.serialization.* |
| import kotlinx.serialization.json.* |
| --> |
| |
| ### Pretty printing |
| |
| By default, the [Json] output is a single line. You can configure it to pretty print the output (that is, add indentations |
| and line breaks for better readability) by setting the [prettyPrint][JsonBuilder.prettyPrint] property to `true`: |
| |
| ```kotlin |
| val format = Json { prettyPrint = true } |
| |
| @Serializable |
| data class Project(val name: String, val language: String) |
| |
| fun main() { |
| val data = Project("kotlinx.serialization", "Kotlin") |
| println(format.encodeToString(data)) |
| } |
| ``` |
| |
| > You can get the full code [here](../guide/example/example-json-01.kt). |
| |
| It gives the following nice result: |
| |
| ```text |
| { |
| "name": "kotlinx.serialization", |
| "language": "Kotlin" |
| } |
| ``` |
| |
| <!--- TEST --> |
| |
| ### Lenient parsing |
| |
| By default, [Json] parser enforces various JSON restrictions to be as specification-compliant as possible |
| (see [RFC-4627]). Particularly, keys and string literals must be quoted. Those restrictions can be relaxed with |
| the [isLenient][JsonBuilder.isLenient] property. With `isLenient = true`, you can parse quite freely-formatted data: |
| |
| ```kotlin |
| val format = Json { isLenient = true } |
| |
| enum class Status { SUPPORTED } |
| |
| @Serializable |
| data class Project(val name: String, val status: Status, val votes: Int) |
| |
| fun main() { |
| val data = format.decodeFromString<Project>(""" |
| { |
| name : kotlinx.serialization, |
| status : SUPPORTED, |
| votes : "9000" |
| } |
| """) |
| println(data) |
| } |
| ``` |
| |
| > You can get the full code [here](../guide/example/example-json-02.kt). |
| |
| You get the object, even though all keys of the source JSON, string, and enum values are unquoted, while an |
| integer is quoted: |
| |
| ```text |
| Project(name=kotlinx.serialization, status=SUPPORTED, votes=9000) |
| ``` |
| |
| <!--- TEST --> |
| |
| ### Ignoring unknown keys |
| |
| JSON format is often used to read the output of third-party services or in other dynamic environments where |
| new properties can be added during the API evolution. By default, unknown keys encountered during deserialization produce an error. |
| You can avoid this and just ignore such keys by setting the [ignoreUnknownKeys][JsonBuilder.ignoreUnknownKeys] property |
| to `true`: |
| |
| ```kotlin |
| val format = Json { ignoreUnknownKeys = true } |
| |
| @Serializable |
| data class Project(val name: String) |
| |
| fun main() { |
| val data = format.decodeFromString<Project>(""" |
| {"name":"kotlinx.serialization","language":"Kotlin"} |
| """) |
| println(data) |
| } |
| ``` |
| |
| > You can get the full code [here](../guide/example/example-json-03.kt). |
| |
| It decodes the object despite the fact that the `Project` class doesn't have the `language` property: |
| |
| ```text |
| Project(name=kotlinx.serialization) |
| ``` |
| |
| <!--- TEST --> |
| |
| ### Alternative Json names |
| |
| It's not a rare case when JSON fields are renamed due to a schema version change. |
| You can use the [`@SerialName` annotation](basic-serialization.md#serial-field-names) to change the name of a JSON field, |
| but such renaming blocks the ability to decode data with the old name. |
| To support multiple JSON names for the one Kotlin property, there is the [JsonNames] annotation: |
| |
| ```kotlin |
| @Serializable |
| data class Project(@JsonNames("title") val name: String) |
| |
| fun main() { |
| val project = Json.decodeFromString<Project>("""{"name":"kotlinx.serialization"}""") |
| println(project) |
| val oldProject = Json.decodeFromString<Project>("""{"title":"kotlinx.coroutines"}""") |
| println(oldProject) |
| } |
| ``` |
| |
| > You can get the full code [here](../guide/example/example-json-04.kt). |
| |
| As you can see, both `name` and `title` Json fields correspond to `name` property: |
| |
| ```text |
| Project(name=kotlinx.serialization) |
| Project(name=kotlinx.coroutines) |
| ``` |
| |
| Support for [JsonNames] annotation is controlled by the [JsonBuilder.useAlternativeNames] flag. |
| Unlike most of the configuration flags, this one is enabled by default and does not need attention |
| unless you want to do some fine-tuning. |
| |
| <!--- TEST --> |
| |
| ### Coercing input values |
| |
| JSON formats that from third parties can evolve, sometimes changing the field types. |
| This can lead to exceptions during decoding when the actual values do not match the expected values. |
| The default [Json] implementation is strict with respect to input types as was demonstrated in |
| the [Type safety is enforced](basic-serialization.md#type-safety-is-enforced) section. You can relax this restriction |
| using the [coerceInputValues][JsonBuilder.coerceInputValues] property. |
| |
| This property only affects decoding. It treats a limited subset of invalid input values as if the |
| corresponding property was missing and uses the default value of the corresponding property instead. |
| The current list of supported invalid values is: |
| |
| * `null` inputs for non-nullable types |
| * unknown values for enums |
| |
| > This list may be expanded in the future, so that [Json] instance configured with this property becomes even more |
| > permissive to invalid value in the input, replacing them with defaults. |
| |
| See the example from the [Type safety is enforced](basic-serialization.md#type-safety-is-enforced) section: |
| |
| ```kotlin |
| 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} |
| """) |
| println(data) |
| } |
| ``` |
| |
| > You can get the full code [here](../guide/example/example-json-05.kt). |
| |
| The invalid `null` value for the `language` property was coerced into the default value: |
| |
| ```text |
| Project(name=kotlinx.serialization, language=Kotlin) |
| ``` |
| |
| <!--- TEST --> |
| |
| |
| ### Encoding defaults |
| |
| Default values of properties are not encoded by default because they will be assigned to missing fields during decoding anyway. |
| See the [Defaults are not encoded](basic-serialization.md#defaults-are-not-encoded-by-default) section for details and an example. |
| This is especially useful for nullable properties with null defaults and avoids writing the corresponding null values. |
| The default behavior can be changed by setting the [encodeDefaults][JsonBuilder.encodeDefaults] property to `true`: |
| |
| ```kotlin |
| 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") |
| println(format.encodeToString(data)) |
| } |
| ``` |
| |
| > You can get the full code [here](../guide/example/example-json-06.kt). |
| |
| It produces the following output which encodes all the property values including the default ones: |
| |
| ```text |
| {"name":"kotlinx.serialization","language":"Kotlin","website":null} |
| ``` |
| |
| <!--- TEST --> |
| |
| ### Explicit nulls |
| |
| By default, all `null` values are encoded into JSON strings, but in some cases you may want to omit them. |
| The encoding of `null` values can be controlled with the [explicitNulls][JsonBuilder.explicitNulls] property. |
| |
| If you set property to `false`, fields with `null` values are not encoded into JSON even if the property does not have a |
| default `null` value. When decoding such JSON, the absence of a property value is treated as `null` for nullable properties |
| without a default value. |
| |
| ```kotlin |
| 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) |
| println(json) |
| println(format.decodeFromString<Project>(json)) |
| } |
| ``` |
| |
| > You can get the full code [here](../guide/example/example-json-07.kt). |
| |
| As you can see, `version`, `website` and `description` fields are not present in output JSON on the first line. |
| After decoding, the missing nullable property `website` without a default values has received a `null` value, |
| while nullable properties `version` and `description` are filled with their default values: |
| |
| ```text |
| {"name":"kotlinx.serialization","language":"Kotlin"} |
| Project(name=kotlinx.serialization, language=Kotlin, version=1.2.2, website=null, description=null) |
| ``` |
| |
| `explicitNulls` is `true` by default as it is the default behavior across different versions of the library. |
| |
| <!--- TEST --> |
| |
| ### Allowing structured map keys |
| |
| JSON format does not natively support the concept of a map with structured keys. Keys in JSON objects |
| are strings and can be used to represent only primitives or enums by default. |
| You can enable non-standard support for structured keys with |
| the [allowStructuredMapKeys][JsonBuilder.allowStructuredMapKeys] property. |
| |
| This is how you can serialize a map with keys of a user-defined class: |
| |
| ```kotlin |
| 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" |
| ) |
| println(format.encodeToString(map)) |
| } |
| ``` |
| |
| > You can get the full code [here](../guide/example/example-json-08.kt). |
| |
| The map with structured keys gets represented as JSON array with the following items: `[key1, value1, key2, value2,...]`. |
| |
| ```text |
| [{"name":"kotlinx.serialization"},"Serialization",{"name":"kotlinx.coroutines"},"Coroutines"] |
| ``` |
| |
| <!--- TEST --> |
| |
| ### Allowing special floating-point values |
| |
| By default, special floating-point values like [Double.NaN] and infinities are not supported in JSON because |
| the JSON specification prohibits it. |
| You can enable their encoding using the [allowSpecialFloatingPointValues][JsonBuilder.allowSpecialFloatingPointValues] |
| property: |
| |
| ```kotlin |
| val format = Json { allowSpecialFloatingPointValues = true } |
| |
| @Serializable |
| class Data( |
| val value: Double |
| ) |
| |
| fun main() { |
| val data = Data(Double.NaN) |
| println(format.encodeToString(data)) |
| } |
| ``` |
| |
| > You can get the full code [here](../guide/example/example-json-09.kt). |
| |
| This example produces the following non-stardard JSON output, yet it is a widely used encoding for |
| special values in JVM world: |
| |
| ```text |
| {"value":NaN} |
| ``` |
| |
| <!--- TEST --> |
| |
| ### Class discriminator for polymorphism |
| |
| A key name that specifies a type when you have a polymorphic data can be specified |
| in the [classDiscriminator][JsonBuilder.classDiscriminator] property: |
| |
| ```kotlin |
| val format = Json { classDiscriminator = "#class" } |
| |
| @Serializable |
| sealed class Project { |
| abstract val name: String |
| } |
| |
| @Serializable |
| @SerialName("owned") |
| class OwnedProject(override val name: String, val owner: String) : Project() |
| |
| fun main() { |
| val data: Project = OwnedProject("kotlinx.coroutines", "kotlin") |
| println(format.encodeToString(data)) |
| } |
| ``` |
| |
| > You can get the full code [here](../guide/example/example-json-10.kt). |
| |
| In combination with an explicitly specified [SerialName] of the class it provides full |
| control over the resulting JSON object: |
| |
| ```text |
| {"#class":"owned","name":"kotlinx.coroutines","owner":"kotlin"} |
| ``` |
| |
| <!--- TEST --> |
| |
| It is also possible to specify different class discriminators for different hierarchies. Instead of Json instance property, use [JsonClassDiscriminator] annotation directly on base serializable class: |
| |
| ```kotlin |
| @Serializable |
| @JsonClassDiscriminator("message_type") |
| sealed class Base |
| ``` |
| |
| This annotation is _inheritable_, so all subclasses of `Base` will have the same discriminator: |
| |
| ```kotlin |
| @Serializable // Class discriminator is inherited from Base |
| sealed class ErrorClass: Base() |
| ``` |
| |
| > To learn more about inheritable serial annotations, see documentation for [InheritableSerialInfo]. |
| |
| Note that it is not possible to explicitly specify different class discriminators in subclasses of `Base`. Only hierarchies with empty intersections can have different discriminators. |
| |
| Discriminator specified in the annotation has priority over discriminator in Json configuration: |
| |
| <!--- INCLUDE |
| |
| @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() |
| --> |
| |
| ```kotlin |
| |
| val format = Json { classDiscriminator = "#class" } |
| |
| fun main() { |
| val data = Message(BaseMessage("not found"), GenericError(404)) |
| println(format.encodeToString(data)) |
| } |
| ``` |
| |
| > You can get the full code [here](../guide/example/example-json-11.kt). |
| |
| As you can see, discriminator from the `Base` class is used: |
| |
| ```text |
| {"message":{"message_type":"my.app.BaseMessage","message":"not found"},"error":{"message_type":"my.app.GenericError","error_code":404}} |
| ``` |
| |
| <!--- TEST --> |
| |
| ### Class discriminator output mode |
| |
| Class discriminator provides information for serializing and deserializing [polymorphic class hierarchies](polymorphism.md#sealed-classes). |
| As shown above, it is only added for polymorphic classes by default. |
| In case you want to encode more or less information for various third party APIs about types in the output, it is possible to control |
| addition of the class discriminator with the [JsonBuilder.classDiscriminatorMode] property. |
| |
| For example, [ClassDiscriminatorMode.NONE] does not add class discriminator at all, in case the receiving party is not interested in Kotlin types: |
| |
| ```kotlin |
| 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") |
| println(format.encodeToString(data)) |
| } |
| ``` |
| |
| > You can get the full code [here](../guide/example/example-json-12.kt). |
| |
| Note that it would be impossible to deserialize this output back with kotlinx.serialization. |
| |
| ```text |
| {"name":"kotlinx.coroutines","owner":"kotlin"} |
| ``` |
| |
| Two other available values are [ClassDiscriminatorMode.POLYMORPHIC] (default behavior) and [ClassDiscriminatorMode.ALL_JSON_OBJECTS] (adds discriminator whenever possible). |
| Consult their documentation for details. |
| |
| <!--- TEST --> |
| |
| ### Decoding enums in a case-insensitive manner |
| |
| [Kotlin's naming policy recommends](https://kotlinlang.org/docs/coding-conventions.html#property-names) naming enum values |
| using either uppercase underscore-separated names or upper camel case names. |
| [Json] uses exact Kotlin enum values names for decoding by default. |
| However, sometimes third-party JSONs have such values named in lowercase or some mixed case. |
| In this case, it is possible to decode enum values in a case-insensitive manner using [JsonBuilder.decodeEnumsCaseInsensitive] property: |
| |
| ```kotlin |
| val format = Json { decodeEnumsCaseInsensitive = true } |
| |
| enum class Cases { VALUE_A, @JsonNames("Alternative") VALUE_B } |
| |
| @Serializable |
| data class CasesList(val cases: List<Cases>) |
| |
| fun main() { |
| println(format.decodeFromString<CasesList>("""{"cases":["value_A", "alternative"]}""")) |
| } |
| ``` |
| |
| > You can get the full code [here](../guide/example/example-json-13.kt). |
| |
| It affects serial names as well as alternative names specified with [JsonNames] annotation, so both values are successfully decoded: |
| |
| ```text |
| CasesList(cases=[VALUE_A, VALUE_B]) |
| ``` |
| |
| This property does not affect encoding in any way. |
| |
| <!--- TEST --> |
| |
| ### Global naming strategy |
| |
| If properties' names in Json input are different from Kotlin ones, it is recommended to specify the name |
| for each property explicitly using [`@SerialName` annotation](basic-serialization.md#serial-field-names). |
| However, there are certain situations where transformation should be applied to every serial name — such as migration |
| from other frameworks or legacy codebase. For that cases, it is possible to specify a [namingStrategy][JsonBuilder.namingStrategy] |
| for a [Json] instance. `kotlinx.serialization` provides one strategy implementation out of the box, the [JsonNamingStrategy.SnakeCase](https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-naming-strategy/-builtins/-snake-case.html): |
| |
| ```kotlin |
| @Serializable |
| data class Project(val projectName: String, val projectOwner: String) |
| |
| val format = Json { namingStrategy = JsonNamingStrategy.SnakeCase } |
| |
| fun main() { |
| val project = format.decodeFromString<Project>("""{"project_name":"kotlinx.coroutines", "project_owner":"Kotlin"}""") |
| println(format.encodeToString(project.copy(projectName = "kotlinx.serialization"))) |
| } |
| ``` |
| |
| > You can get the full code [here](../guide/example/example-json-14.kt). |
| |
| As you can see, both serialization and deserialization work as if all serial names are transformed from camel case to snake case: |
| |
| ```text |
| {"project_name":"kotlinx.serialization","project_owner":"Kotlin"} |
| ``` |
| |
| There are some caveats one should remember while dealing with a [JsonNamingStrategy]: |
| |
| * Due to the nature of the `kotlinx.serialization` framework, naming strategy transformation is applied to all properties regardless |
| of whether their serial name was taken from the property name or provided by [SerialName] annotation. |
| Effectively, it means one cannot avoid transformation by explicitly specifying the serial name. To be able to deserialize |
| non-transformed names, [JsonNames] annotation can be used instead. |
| |
| * Collision of the transformed name with any other (transformed) properties serial names or any alternative names |
| specified with [JsonNames] will lead to a deserialization exception. |
| |
| * Global naming strategies are very implicit: by looking only at the definition of the class, |
| it is impossible to determine which names it will have in the serialized form. |
| As a consequence, naming strategies are not friendly to actions like Find Usages/Rename in IDE, full-text search by grep, etc. |
| For them, the original name and the transformed are two different things; |
| changing one without the other may introduce bugs in many unexpected ways and lead to greater maintenance efforts for code with global naming strategies. |
| |
| Therefore, one should carefully weigh the pros and cons before considering adding global naming strategies to an application. |
| |
| <!--- TEST --> |
| |
| ## Json elements |
| |
| Aside from direct conversions between strings and JSON objects, Kotlin serialization offers APIs that allow |
| other ways of working with JSON in the code. For example, you might need to tweak the data before it can parse |
| or otherwise work with such an unstructured data that it does not readily fit into the typesafe world of Kotlin |
| serialization. |
| |
| The main concept in this part of the library is [JsonElement]. Read on to learn what you can do with it. |
| |
| ### Parsing to Json element |
| |
| A string can be _parsed_ into an instance of [JsonElement] with the [Json.parseToJsonElement] function. |
| It is called neither decoding nor deserialization because none of that happens in the process. |
| It just parses a JSON and forms an object representing it: |
| |
| ```kotlin |
| fun main() { |
| val element = Json.parseToJsonElement(""" |
| {"name":"kotlinx.serialization","language":"Kotlin"} |
| """) |
| println(element) |
| } |
| ``` |
| |
| > You can get the full code [here](../guide/example/example-json-15.kt). |
| |
| A `JsonElement` prints itself as a valid JSON: |
| |
| ```text |
| {"name":"kotlinx.serialization","language":"Kotlin"} |
| ``` |
| |
| <!--- TEST --> |
| |
| ### Types of Json elements |
| |
| A [JsonElement] class has three direct subtypes, closely following JSON grammar: |
| |
| * [JsonPrimitive] represents primitive JSON elements, such as string, number, boolean, and null. |
| Each primitive has a simple string [content][JsonPrimitive.content]. There is also a |
| [JsonPrimitive()] constructor function overloaded to accept various primitive Kotlin types and |
| to convert them to `JsonPrimitive`. |
| |
| * [JsonArray] represents a JSON `[...]` array. It is a Kotlin [List] of `JsonElement` items. |
| |
| * [JsonObject] represents a JSON `{...}` object. It is a Kotlin [Map] from `String` keys to `JsonElement` values. |
| |
| The `JsonElement` class has extensions that cast it to its corresponding subtypes: |
| [jsonPrimitive][_jsonPrimitive], [jsonArray][_jsonArray], [jsonObject][_jsonObject]. The `JsonPrimitive` class, |
| in turn, provides converters to Kotlin primitive types: [int], [intOrNull], [long], [longOrNull], |
| and similar ones for other types. This is how you can use them for processing JSON whose structure you know: |
| |
| ```kotlin |
| fun main() { |
| val element = Json.parseToJsonElement(""" |
| { |
| "name": "kotlinx.serialization", |
| "forks": [{"votes": 42}, {"votes": 9000}, {}] |
| } |
| """) |
| val sum = element |
| .jsonObject["forks"]!! |
| .jsonArray.sumOf { it.jsonObject["votes"]?.jsonPrimitive?.int ?: 0 } |
| println(sum) |
| } |
| ``` |
| |
| > You can get the full code [here](../guide/example/example-json-16.kt). |
| |
| The above example sums `votes` in all objects in the `forks` array, ignoring the objects that have no `votes`: |
| |
| ```text |
| 9042 |
| ``` |
| |
| <!--- TEST --> |
| |
| Note that the execution will fail if the structure of the data is otherwise different. |
| |
| ### Json element builders |
| |
| You can construct instances of specific [JsonElement] subtypes using the respective builder functions |
| [buildJsonArray] and [buildJsonObject]. They provide a DSL to define the resulting JSON structure. It |
| is similar to Kotlin standard library collection builders, but with a JSON-specific convenience |
| of more type-specific overloads and inner builder functions. The following example shows |
| all the key features: |
| |
| ```kotlin |
| fun main() { |
| val element = buildJsonObject { |
| put("name", "kotlinx.serialization") |
| putJsonObject("owner") { |
| put("name", "kotlin") |
| } |
| putJsonArray("forks") { |
| addJsonObject { |
| put("votes", 42) |
| } |
| addJsonObject { |
| put("votes", 9000) |
| } |
| } |
| } |
| println(element) |
| } |
| ``` |
| |
| > You can get the full code [here](../guide/example/example-json-17.kt). |
| |
| As a result, you get a proper JSON string: |
| |
| ```text |
| {"name":"kotlinx.serialization","owner":{"name":"kotlin"},"forks":[{"votes":42},{"votes":9000}]} |
| ``` |
| |
| <!--- TEST --> |
| |
| ### Decoding Json elements |
| |
| An instance of the [JsonElement] class can be decoded into a serializable object using |
| the [Json.decodeFromJsonElement] function: |
| |
| ```kotlin |
| @Serializable |
| data class Project(val name: String, val language: String) |
| |
| fun main() { |
| val element = buildJsonObject { |
| put("name", "kotlinx.serialization") |
| put("language", "Kotlin") |
| } |
| val data = Json.decodeFromJsonElement<Project>(element) |
| println(data) |
| } |
| ``` |
| |
| > You can get the full code [here](../guide/example/example-json-18.kt). |
| |
| The result is exactly what you would expect: |
| |
| ```text |
| Project(name=kotlinx.serialization, language=Kotlin) |
| ``` |
| |
| <!--- TEST --> |
| |
| ### Encoding literal Json content (experimental) |
| |
| > This functionality is experimental and requires opting-in to [the experimental Kotlinx Serialization API](compatibility.md#experimental-api). |
| |
| In some cases it might be necessary to encode an arbitrary unquoted value. |
| This can be achieved with [JsonUnquotedLiteral]. |
| |
| #### Serializing large decimal numbers |
| |
| The JSON specification does not restrict the size or precision of numbers, however it is not possible to serialize |
| numbers of arbitrary size or precision using [JsonPrimitive()]. |
| |
| If [Double] is used, then the numbers are limited in precision, meaning that large numbers are truncated. |
| When using Kotlin/JVM [BigDecimal] can be used instead, but [JsonPrimitive()] will encode the value as a string, not a |
| number. |
| |
| ```kotlin |
| import java.math.BigDecimal |
| |
| val format = Json { prettyPrint = true } |
| |
| fun main() { |
| val pi = BigDecimal("3.141592653589793238462643383279") |
| |
| val piJsonDouble = JsonPrimitive(pi.toDouble()) |
| val piJsonString = JsonPrimitive(pi.toString()) |
| |
| val piObject = buildJsonObject { |
| put("pi_double", piJsonDouble) |
| put("pi_string", piJsonString) |
| } |
| |
| println(format.encodeToString(piObject)) |
| } |
| ``` |
| |
| > You can get the full code [here](../guide/example/example-json-19.kt). |
| |
| Even though `pi` was defined as a number with 30 decimal places, the resulting JSON does not reflect this. |
| The [Double] value is truncated to 15 decimal places, and the String is wrapped in quotes - which is not a JSON number. |
| |
| ```text |
| { |
| "pi_double": 3.141592653589793, |
| "pi_string": "3.141592653589793238462643383279" |
| } |
| ``` |
| |
| <!--- TEST --> |
| |
| To avoid precision loss, the string value of `pi` can be encoded using [JsonUnquotedLiteral]. |
| |
| ```kotlin |
| import java.math.BigDecimal |
| |
| val format = Json { prettyPrint = true } |
| |
| fun main() { |
| val pi = BigDecimal("3.141592653589793238462643383279") |
| |
| // use JsonUnquotedLiteral to encode raw JSON content |
| val piJsonLiteral = JsonUnquotedLiteral(pi.toString()) |
| |
| 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) |
| } |
| |
| println(format.encodeToString(piObject)) |
| } |
| ``` |
| |
| > You can get the full code [here](../guide/example/example-json-20.kt). |
| |
| `pi_literal` now accurately matches the value defined. |
| |
| ```text |
| { |
| "pi_literal": 3.141592653589793238462643383279, |
| "pi_double": 3.141592653589793, |
| "pi_string": "3.141592653589793238462643383279" |
| } |
| ``` |
| |
| <!--- TEST --> |
| |
| To decode `pi` back to a [BigDecimal], the string content of the [JsonPrimitive] can be used. |
| |
| (This demonstration uses a [JsonPrimitive] for simplicity. For a more re-usable method of handling serialization, see |
| [Json Transformations](#json-transformations) below.) |
| |
| |
| ```kotlin |
| import java.math.BigDecimal |
| |
| fun main() { |
| val piObjectJson = """ |
| { |
| "pi_literal": 3.141592653589793238462643383279 |
| } |
| """.trimIndent() |
| |
| val piObject: JsonObject = Json.decodeFromString(piObjectJson) |
| |
| val piJsonLiteral = piObject["pi_literal"]!!.jsonPrimitive.content |
| |
| val pi = BigDecimal(piJsonLiteral) |
| |
| println(pi) |
| } |
| ``` |
| |
| > You can get the full code [here](../guide/example/example-json-21.kt). |
| |
| The exact value of `pi` is decoded, with all 30 decimal places of precision that were in the source JSON. |
| |
| ```text |
| 3.141592653589793238462643383279 |
| ``` |
| |
| <!--- TEST --> |
| |
| #### Using `JsonUnquotedLiteral` to create a literal unquoted value of `null` is forbidden |
| |
| To avoid creating an inconsistent state, encoding a String equal to `"null"` is forbidden. |
| Use [JsonNull] or [JsonPrimitive] instead. |
| |
| ```kotlin |
| fun main() { |
| // caution: creating null with JsonUnquotedLiteral will cause an exception! |
| JsonUnquotedLiteral("null") |
| } |
| ``` |
| |
| > You can get the full code [here](../guide/example/example-json-22.kt). |
| |
| ```text |
| Exception in thread "main" kotlinx.serialization.json.internal.JsonEncodingException: Creating a literal unquoted value of 'null' is forbidden. If you want to create JSON null literal, use JsonNull object, otherwise, use JsonPrimitive |
| ``` |
| |
| <!--- TEST LINES_START --> |
| |
| |
| ## Json transformations |
| |
| To affect the shape and contents of JSON output after serialization, or adapt input to deserialization, |
| it is possible to write a [custom serializer](serializers.md). However, it may be inconvenient to |
| carefully follow [Encoder] and [Decoder] calling conventions, especially for relatively small and easy tasks. |
| For that purpose, Kotlin serialization provides an API that can reduce the burden of implementing a custom |
| serializer to a problem of manipulating a Json elements tree. |
| |
| We recommend that you get familiar with the [Serializers](serializers.md) chapter: among other things, it |
| explains how custom serializers are bound to classes. |
| |
| Transformation capabilities are provided by the abstract [JsonTransformingSerializer] class which implements [KSerializer]. |
| Instead of direct interaction with `Encoder` or `Decoder`, this class asks you to supply transformations for JSON tree |
| represented by the [JsonElement] class using the`transformSerialize` and |
| `transformDeserialize` methods. Let's take a look at the examples. |
| |
| ### Array wrapping |
| |
| The first example is an implementation of JSON array wrapping for lists. |
| |
| Consider a REST API that returns a JSON array of `User` objects, or a single object (not wrapped into an array) if there |
| is only one element in the result. |
| |
| In the data model, use the [`@Serializable`][Serializable] annotation to specify a custom serializer for a |
| `users: List<User>` property. |
| |
| <!--- INCLUDE |
| import kotlinx.serialization.builtins.* |
| --> |
| |
| ```kotlin |
| @Serializable |
| data class Project( |
| val name: String, |
| @Serializable(with = UserListSerializer::class) |
| val users: List<User> |
| ) |
| |
| @Serializable |
| data class User(val name: String) |
| ``` |
| |
| Since this example covers only the deserialization case, you can implement `UserListSerializer` and override only the |
| `transformDeserialize` function. The `JsonTransformingSerializer` constructor takes an original serializer |
| as parameter (this approach is shown in the section [Constructing collection serializers](serializers.md#constructing-collection-serializers)): |
| |
| ```kotlin |
| object UserListSerializer : JsonTransformingSerializer<List<User>>(ListSerializer(User.serializer())) { |
| // If response is not an array, then it is a single object that should be wrapped into the array |
| override fun transformDeserialize(element: JsonElement): JsonElement = |
| if (element !is JsonArray) JsonArray(listOf(element)) else element |
| } |
| ``` |
| |
| Now you can test the code with a JSON array or a single JSON object as inputs. |
| |
| ```kotlin |
| fun main() { |
| println(Json.decodeFromString<Project>(""" |
| {"name":"kotlinx.serialization","users":{"name":"kotlin"}} |
| """)) |
| println(Json.decodeFromString<Project>(""" |
| {"name":"kotlinx.serialization","users":[{"name":"kotlin"},{"name":"jetbrains"}]} |
| """)) |
| } |
| ``` |
| |
| > You can get the full code [here](../guide/example/example-json-23.kt). |
| |
| The output shows that both cases are correctly deserialized into a Kotlin [List]. |
| |
| ```text |
| Project(name=kotlinx.serialization, users=[User(name=kotlin)]) |
| Project(name=kotlinx.serialization, users=[User(name=kotlin), User(name=jetbrains)]) |
| ``` |
| |
| <!--- TEST --> |
| |
| ### Array unwrapping |
| |
| You can also implement the `transformSerialize` function to unwrap a single-element list into a single JSON object |
| during serialization: |
| |
| <!--- INCLUDE |
| import kotlinx.serialization.builtins.* |
| |
| @Serializable |
| data class Project( |
| val name: String, |
| @Serializable(with = UserListSerializer::class) |
| val users: List<User> |
| ) |
| |
| @Serializable |
| data class User(val name: String) |
| |
| object UserListSerializer : JsonTransformingSerializer<List<User>>(ListSerializer(User.serializer())) { |
| --> |
| |
| ```kotlin |
| override fun transformSerialize(element: JsonElement): JsonElement { |
| require(element is JsonArray) // this serializer is used only with lists |
| return element.singleOrNull() ?: element |
| } |
| ``` |
| |
| <!--- INCLUDE |
| } |
| --> |
| |
| Now, if you serialize a single-element list of objects from Kotlin: |
| |
| ```kotlin |
| fun main() { |
| val data = Project("kotlinx.serialization", listOf(User("kotlin"))) |
| println(Json.encodeToString(data)) |
| } |
| ``` |
| |
| > You can get the full code [here](../guide/example/example-json-24.kt). |
| |
| You end up with a single JSON object, not an array with one element: |
| |
| ```text |
| {"name":"kotlinx.serialization","users":{"name":"kotlin"}} |
| ``` |
| |
| <!--- TEST --> |
| |
| ### Manipulating default values |
| |
| Another kind of useful transformation is omitting specific values from the output JSON, for example, if it |
| is used as default when missing or for other reasons. |
| |
| Imagine that you cannot specify a default value for the `language` property in the `Project` data model for some reason, |
| but you need it omitted from the JSON when it is equal to `Kotlin` (we can all agree that Kotlin should be default anyway). |
| You can fix it by writing the special `ProjectSerializer` based on |
| the [Plugin-generated serializer](serializers.md#plugin-generated-serializer) for the `Project` class. |
| |
| ```kotlin |
| @Serializable |
| class Project(val name: String, val language: String) |
| |
| object ProjectSerializer : JsonTransformingSerializer<Project>(Project.serializer()) { |
| override fun transformSerialize(element: JsonElement): JsonElement = |
| // Filter out top-level key value pair with the key "language" and the value "Kotlin" |
| JsonObject(element.jsonObject.filterNot { |
| (k, v) -> k == "language" && v.jsonPrimitive.content == "Kotlin" |
| }) |
| } |
| ``` |
| |
| In the example below, we are serializing the `Project` class at the top-level, so we explicitly |
| pass the above `ProjectSerializer` to [Json.encodeToString] function as was shown in |
| the [Passing a serializer manually](serializers.md#passing-a-serializer-manually) section: |
| |
| ```kotlin |
| fun main() { |
| val data = Project("kotlinx.serialization", "Kotlin") |
| println(Json.encodeToString(data)) // using plugin-generated serializer |
| println(Json.encodeToString(ProjectSerializer, data)) // using custom serializer |
| } |
| ``` |
| |
| > You can get the full code [here](../guide/example/example-json-25.kt). |
| |
| See the effect of the custom serializer: |
| |
| ```text |
| {"name":"kotlinx.serialization","language":"Kotlin"} |
| {"name":"kotlinx.serialization"} |
| ``` |
| |
| <!--- TEST --> |
| |
| ### Content-based polymorphic deserialization |
| |
| Typically, [polymorphic serialization](polymorphism.md) requires a dedicated `"type"` key |
| (also known as _class discriminator_) in the incoming JSON object to determine the actual serializer |
| which should be used to deserialize Kotlin class. |
| |
| However, sometimes the `type` property may not be present in the input. In this case, you need to guess |
| the actual type by the shape of JSON, for example by the presence of a specific key. |
| |
| [JsonContentPolymorphicSerializer] provides a skeleton implementation for such a strategy. |
| To use it, override its `selectDeserializer` method. |
| Let's start with the following class hierarchy. |
| |
| > Note that is does not have to be `sealed` as recommended in the [Sealed classes](polymorphism.md#sealed-classes) section, |
| > because we are not going to take advantage of the plugin-generated code that automatically selects the |
| > appropriate subclass, but are going to implement this code manually. |
| |
| <!--- INCLUDE |
| import kotlinx.serialization.builtins.* |
| --> |
| |
| ```kotlin |
| @Serializable |
| abstract class Project { |
| abstract val name: String |
| } |
| |
| @Serializable |
| data class BasicProject(override val name: String): Project() |
| |
| |
| @Serializable |
| data class OwnedProject(override val name: String, val owner: String) : Project() |
| ``` |
| |
| You can distinguish the `BasicProject` and `OwnedProject` subclasses by the presence of |
| the `owner` key in the JSON object. |
| |
| ```kotlin |
| object ProjectSerializer : JsonContentPolymorphicSerializer<Project>(Project::class) { |
| override fun selectDeserializer(element: JsonElement) = when { |
| "owner" in element.jsonObject -> OwnedProject.serializer() |
| else -> BasicProject.serializer() |
| } |
| } |
| ``` |
| |
| When you use this serializer to serialize data, either [registered](polymorphism.md#registered-subclasses) or |
| the default serializer is selected for the actual type at runtime: |
| |
| ```kotlin |
| fun main() { |
| val data = listOf( |
| OwnedProject("kotlinx.serialization", "kotlin"), |
| BasicProject("example") |
| ) |
| val string = Json.encodeToString(ListSerializer(ProjectSerializer), data) |
| println(string) |
| println(Json.decodeFromString(ListSerializer(ProjectSerializer), string)) |
| } |
| ``` |
| |
| > You can get the full code [here](../guide/example/example-json-26.kt). |
| |
| No class discriminator is added in the JSON output: |
| |
| ```text |
| [{"name":"kotlinx.serialization","owner":"kotlin"},{"name":"example"}] |
| [OwnedProject(name=kotlinx.serialization, owner=kotlin), BasicProject(name=example)] |
| ``` |
| |
| <!--- TEST --> |
| |
| ### Under the hood (experimental) |
| |
| Although abstract serializers mentioned above can cover most of the cases, it is possible to implement similar machinery |
| manually, using only the [KSerializer] class. |
| If tweaking the abstract methods `transformSerialize`/`transformDeserialize`/`selectDeserializer` is not enough, |
| then altering `serialize`/`deserialize` is a way to go. |
| |
| Here are some useful things about custom serializers with [Json]: |
| |
| * [Encoder] can be cast to [JsonEncoder], and [Decoder] to [JsonDecoder], if the current format is [Json]. |
| * `JsonDecoder` has the [decodeJsonElement][JsonDecoder.decodeJsonElement] method and `JsonEncoder` |
| has the [encodeJsonElement][JsonEncoder.encodeJsonElement] method, |
| which basically retrieve an element from and insert an element to a current position in the stream. |
| * Both [`JsonDecoder`][JsonDecoder.json] and [`JsonEncoder`][JsonEncoder.json] have the `json` property, |
| which returns [Json] instance with all settings that are currently in use. |
| * [Json] has the [encodeToJsonElement][Json.encodeToJsonElement] and [decodeFromJsonElement][Json.decodeFromJsonElement] methods. |
| |
| Given all that, it is possible to implement two-stage conversion `Decoder -> JsonElement -> value` or |
| `value -> JsonElement -> Encoder`. |
| For example, you can implement a fully custom serializer for the following `Response` class so that its |
| `Ok` subclass is represented directly, but the `Error` subclass is represented by an object with the error message: |
| |
| <!--- INCLUDE |
| import kotlinx.serialization.descriptors.* |
| import kotlinx.serialization.encoding.* |
| --> |
| |
| ```kotlin |
| @Serializable(with = ResponseSerializer::class) |
| sealed class Response<out T> { |
| data class Ok<out T>(val data: T) : Response<T>() |
| data class Error(val message: String) : Response<Nothing>() |
| } |
| |
| class ResponseSerializer<T>(private val dataSerializer: KSerializer<T>) : KSerializer<Response<T>> { |
| override val descriptor: SerialDescriptor = buildSerialDescriptor("Response", PolymorphicKind.SEALED) { |
| element("Ok", dataSerializer.descriptor) |
| element("Error", buildClassSerialDescriptor("Error") { |
| element<String>("message") |
| }) |
| } |
| |
| override fun deserialize(decoder: Decoder): Response<T> { |
| // Decoder -> JsonDecoder |
| require(decoder is JsonDecoder) // this class can be decoded only by Json |
| // JsonDecoder -> JsonElement |
| val element = decoder.decodeJsonElement() |
| // JsonElement -> value |
| if (element is JsonObject && "error" in element) |
| return Response.Error(element["error"]!!.jsonPrimitive.content) |
| return Response.Ok(decoder.json.decodeFromJsonElement(dataSerializer, element)) |
| } |
| |
| override fun serialize(encoder: Encoder, value: Response<T>) { |
| // Encoder -> JsonEncoder |
| require(encoder is JsonEncoder) // This class can be encoded only by Json |
| // value -> JsonElement |
| val element = when (value) { |
| is Response.Ok -> encoder.json.encodeToJsonElement(dataSerializer, value.data) |
| is Response.Error -> buildJsonObject { put("error", value.message) } |
| } |
| // JsonElement -> JsonEncoder |
| encoder.encodeJsonElement(element) |
| } |
| } |
| ``` |
| |
| Having this serializable `Response` implementation, you can take any serializable payload for its data |
| and serialize or deserialize the corresponding responses: |
| |
| ```kotlin |
| @Serializable |
| data class Project(val name: String) |
| |
| fun main() { |
| val responses = listOf( |
| Response.Ok(Project("kotlinx.serialization")), |
| Response.Error("Not found") |
| ) |
| val string = Json.encodeToString(responses) |
| println(string) |
| println(Json.decodeFromString<List<Response<Project>>>(string)) |
| } |
| ``` |
| |
| > You can get the full code [here](../guide/example/example-json-27.kt). |
| |
| This gives you fine-grained control on the representation of the `Response` class in the JSON output: |
| |
| ```text |
| [{"name":"kotlinx.serialization"},{"error":"Not found"}] |
| [Ok(data=Project(name=kotlinx.serialization)), Error(message=Not found)] |
| ``` |
| |
| <!--- TEST --> |
| |
| ### Maintaining custom JSON attributes |
| |
| A good example of custom JSON-specific serializer would be a deserializer |
| that packs all unknown JSON properties into a dedicated field of `JsonObject` type. |
| |
| Let's add `UnknownProject` – a class with the `name` property and arbitrary details flattened into the same object: |
| |
| <!--- INCLUDE |
| import kotlinx.serialization.descriptors.* |
| import kotlinx.serialization.encoding.* |
| --> |
| |
| ```kotlin |
| data class UnknownProject(val name: String, val details: JsonObject) |
| ``` |
| |
| However, the default plugin-generated serializer requires details |
| to be a separate JSON object and that's not what we want. |
| |
| To mitigate that, write an own serializer that uses the fact that it works only with the `Json` format: |
| |
| ```kotlin |
| object UnknownProjectSerializer : KSerializer<UnknownProject> { |
| override val descriptor: SerialDescriptor = buildClassSerialDescriptor("UnknownProject") { |
| element<String>("name") |
| element<JsonElement>("details") |
| } |
| |
| override fun deserialize(decoder: Decoder): UnknownProject { |
| // Cast to JSON-specific interface |
| val jsonInput = decoder as? JsonDecoder ?: error("Can be deserialized only by JSON") |
| // Read the whole content as JSON |
| val json = jsonInput.decodeJsonElement().jsonObject |
| // Extract and remove name property |
| val name = json.getValue("name").jsonPrimitive.content |
| val details = json.toMutableMap() |
| details.remove("name") |
| return UnknownProject(name, JsonObject(details)) |
| } |
| |
| override fun serialize(encoder: Encoder, value: UnknownProject) { |
| error("Serialization is not supported") |
| } |
| } |
| ``` |
| |
| Now it can be used to read flattened JSON details as `UnknownProject`: |
| |
| ```kotlin |
| fun main() { |
| println(Json.decodeFromString(UnknownProjectSerializer, """{"type":"unknown","name":"example","maintainer":"Unknown","license":"Apache 2.0"}""")) |
| } |
| ``` |
| |
| > You can get the full code [here](../guide/example/example-json-28.kt). |
| |
| ```text |
| UnknownProject(name=example, details={"type":"unknown","maintainer":"Unknown","license":"Apache 2.0"}) |
| ``` |
| |
| <!--- TEST --> |
| |
| --- |
| |
| The next chapter covers [Alternative and custom formats (experimental)](formats.md). |
| |
| |
| <!-- references --> |
| [RFC-4627]: https://www.ietf.org/rfc/rfc4627.txt |
| [BigDecimal]: https://docs.oracle.com/javase/8/docs/api/java/math/BigDecimal.html |
| |
| <!-- stdlib references --> |
| [Double]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-double/ |
| [Double.NaN]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-double/-na-n.html |
| [List]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/ |
| [Map]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-map/ |
| |
| <!--- MODULE /kotlinx-serialization-core --> |
| <!--- INDEX kotlinx-serialization-core/kotlinx.serialization --> |
| |
| [SerialName]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serial-name/index.html |
| [InheritableSerialInfo]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-inheritable-serial-info/index.html |
| [KSerializer]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-k-serializer/index.html |
| [Serializable]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serializable/index.html |
| |
| <!--- INDEX kotlinx-serialization-core/kotlinx.serialization.encoding --> |
| |
| [Encoder]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/index.html |
| [Decoder]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-decoder/index.html |
| |
| <!--- MODULE /kotlinx-serialization-json --> |
| <!--- INDEX kotlinx-serialization-json/kotlinx.serialization.json --> |
| |
| [Json]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/index.html |
| [Json()]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json.html |
| [JsonBuilder]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/index.html |
| [JsonBuilder.prettyPrint]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/pretty-print.html |
| [JsonBuilder.isLenient]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/is-lenient.html |
| [JsonBuilder.ignoreUnknownKeys]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/ignore-unknown-keys.html |
| [JsonNames]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-names/index.html |
| [JsonBuilder.useAlternativeNames]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/use-alternative-names.html |
| [JsonBuilder.coerceInputValues]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/coerce-input-values.html |
| [JsonBuilder.encodeDefaults]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/encode-defaults.html |
| [JsonBuilder.explicitNulls]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/explicit-nulls.html |
| [JsonBuilder.allowStructuredMapKeys]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/allow-structured-map-keys.html |
| [JsonBuilder.allowSpecialFloatingPointValues]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/allow-special-floating-point-values.html |
| [JsonBuilder.classDiscriminator]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/class-discriminator.html |
| [JsonClassDiscriminator]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-class-discriminator/index.html |
| [JsonBuilder.classDiscriminatorMode]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/class-discriminator-mode.html |
| [ClassDiscriminatorMode.NONE]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-class-discriminator-mode/-n-o-n-e/index.html |
| [ClassDiscriminatorMode.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/index.html |
| [ClassDiscriminatorMode.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/index.html |
| [JsonBuilder.decodeEnumsCaseInsensitive]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/decode-enums-case-insensitive.html |
| [JsonBuilder.namingStrategy]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/naming-strategy.html |
| [JsonNamingStrategy]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-naming-strategy/index.html |
| [JsonElement]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-element/index.html |
| [Json.parseToJsonElement]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/parse-to-json-element.html |
| [JsonPrimitive]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-primitive/index.html |
| [JsonPrimitive.content]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-primitive/content.html |
| [JsonPrimitive()]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-primitive.html |
| [JsonArray]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-array/index.html |
| [JsonObject]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-object/index.html |
| [_jsonPrimitive]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/json-primitive.html |
| [_jsonArray]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/json-array.html |
| [_jsonObject]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/json-object.html |
| [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 |
| [buildJsonArray]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/build-json-array.html |
| [buildJsonObject]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/build-json-object.html |
| [Json.decodeFromJsonElement]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/decode-from-json-element.html |
| [JsonUnquotedLiteral]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-unquoted-literal.html |
| [JsonNull]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-null/index.html |
| [JsonTransformingSerializer]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-transforming-serializer/index.html |
| [Json.encodeToString]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/encode-to-string.html |
| [JsonContentPolymorphicSerializer]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-content-polymorphic-serializer/index.html |
| [JsonEncoder]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-encoder/index.html |
| [JsonDecoder]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-decoder/index.html |
| [JsonDecoder.decodeJsonElement]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-decoder/decode-json-element.html |
| [JsonEncoder.encodeJsonElement]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-encoder/encode-json-element.html |
| [JsonDecoder.json]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-decoder/json.html |
| [JsonEncoder.json]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-encoder/json.html |
| [Json.encodeToJsonElement]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/encode-to-json-element.html |
| |
| <!--- END --> |