• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1<!--- TEST_NAME JsonTest -->
2
3# JSON features
4
5This is the fifth chapter of the [Kotlin Serialization Guide](serialization-guide.md).
6In this chapter, we'll walk through features of [JSON](https://www.json.org/json-en.html) serialization available in the [Json] class.
7
8**Table of contents**
9
10<!--- TOC -->
11
12* [Json configuration](#json-configuration)
13  * [Pretty printing](#pretty-printing)
14  * [Lenient parsing](#lenient-parsing)
15  * [Ignoring unknown keys](#ignoring-unknown-keys)
16  * [Alternative Json names](#alternative-json-names)
17  * [Encoding defaults](#encoding-defaults)
18  * [Explicit nulls](#explicit-nulls)
19  * [Coercing input values](#coercing-input-values)
20  * [Allowing structured map keys](#allowing-structured-map-keys)
21  * [Allowing special floating-point values](#allowing-special-floating-point-values)
22  * [Class discriminator for polymorphism](#class-discriminator-for-polymorphism)
23  * [Class discriminator output mode](#class-discriminator-output-mode)
24  * [Decoding enums in a case-insensitive manner](#decoding-enums-in-a-case-insensitive-manner)
25  * [Global naming strategy](#global-naming-strategy)
26  * [Base64](#base64)
27* [Json elements](#json-elements)
28  * [Parsing to Json element](#parsing-to-json-element)
29  * [Types of Json elements](#types-of-json-elements)
30  * [Json element builders](#json-element-builders)
31  * [Decoding Json elements](#decoding-json-elements)
32  * [Encoding literal Json content (experimental)](#encoding-literal-json-content-experimental)
33    * [Serializing large decimal numbers](#serializing-large-decimal-numbers)
34    * [Using `JsonUnquotedLiteral` to create a literal unquoted value of `null` is forbidden](#using-jsonunquotedliteral-to-create-a-literal-unquoted-value-of-null-is-forbidden)
35* [Json transformations](#json-transformations)
36  * [Array wrapping](#array-wrapping)
37  * [Array unwrapping](#array-unwrapping)
38  * [Manipulating default values](#manipulating-default-values)
39  * [Content-based polymorphic deserialization](#content-based-polymorphic-deserialization)
40  * [Extending the behavior of the plugin generated serializer](#extending-the-behavior-of-the-plugin-generated-serializer)
41  * [Under the hood (experimental)](#under-the-hood-experimental)
42  * [Maintaining custom JSON attributes](#maintaining-custom-json-attributes)
43
44<!--- END -->
45
46## Json configuration
47
48The default [Json] implementation is quite strict with respect to invalid inputs. It enforces Kotlin type safety and
49restricts Kotlin values that can be serialized so that the resulting JSON representations are standard.
50Many non-standard JSON features are supported by creating a custom instance of a JSON _format_.
51
52To use a custom JSON format configuration, create your own [Json] class instance from an existing
53instance, such as a default `Json` object, using the [Json()] builder function. Specify parameter values
54in the parentheses via the [JsonBuilder] DSL. The resulting `Json` format instance is immutable and thread-safe;
55it can be simply stored in a top-level property.
56
57> We recommend that you store and reuse custom instances of formats for performance reasons because format implementations
58> may cache format-specific additional information about the classes they serialize.
59
60This chapter shows configuration features that [Json] supports.
61
62<!--- INCLUDE .*-json-.*
63import kotlinx.serialization.*
64import kotlinx.serialization.json.*
65-->
66
67### Pretty printing
68
69By default, the [Json] output is a single line. You can configure it to pretty print the output (that is, add indentations
70and line breaks for better readability) by setting the [prettyPrint][JsonBuilder.prettyPrint] property to `true`:
71
72```kotlin
73val format = Json { prettyPrint = true }
74
75@Serializable
76data class Project(val name: String, val language: String)
77
78fun main() {
79    val data = Project("kotlinx.serialization", "Kotlin")
80    println(format.encodeToString(data))
81}
82```
83
84> You can get the full code [here](../guide/example/example-json-01.kt).
85
86It gives the following nice result:
87
88```text
89{
90    "name": "kotlinx.serialization",
91    "language": "Kotlin"
92}
93```
94
95<!--- TEST -->
96
97### Lenient parsing
98
99By default, [Json] parser enforces various JSON restrictions to be as specification-compliant as possible
100(see [RFC-4627]). Particularly, keys and string literals must be quoted. Those restrictions can be relaxed with
101the [isLenient][JsonBuilder.isLenient] property. With `isLenient = true`, you can parse quite freely-formatted data:
102
103```kotlin
104val format = Json { isLenient = true }
105
106enum class Status { SUPPORTED }
107
108@Serializable
109data class Project(val name: String, val status: Status, val votes: Int)
110
111fun main() {
112    val data = format.decodeFromString<Project>("""
113        {
114            name   : kotlinx.serialization,
115            status : SUPPORTED,
116            votes  : "9000"
117        }
118    """)
119    println(data)
120}
121```
122
123> You can get the full code [here](../guide/example/example-json-02.kt).
124
125You get the object, even though all keys of the source JSON, string and enum values are unquoted:
126
127```text
128Project(name=kotlinx.serialization, status=SUPPORTED, votes=9000)
129```
130
131> Note that parsing of quoted numbers or booleans such as `votes: "9000"` to `val votes: Int` is generally allowed by kotlinx.serialization
132> regardless of the `isLenient` flag, since such JSON is syntactically valid.
133
134<!--- TEST -->
135
136### Ignoring unknown keys
137
138JSON format is often used to read the output of third-party services or in other dynamic environments where
139new properties can be added during the API evolution. By default, unknown keys encountered during deserialization produce an error.
140You can avoid this and just ignore such keys by setting the [ignoreUnknownKeys][JsonBuilder.ignoreUnknownKeys] property
141to `true`:
142
143```kotlin
144val format = Json { ignoreUnknownKeys = true }
145
146@Serializable
147data class Project(val name: String)
148
149fun main() {
150    val data = format.decodeFromString<Project>("""
151        {"name":"kotlinx.serialization","language":"Kotlin"}
152    """)
153    println(data)
154}
155```
156
157> You can get the full code [here](../guide/example/example-json-03.kt).
158
159It decodes the object despite the fact that the `Project` class doesn't have the `language` property:
160
161```text
162Project(name=kotlinx.serialization)
163```
164
165<!--- TEST -->
166
167### Alternative Json names
168
169It's not a rare case when JSON fields are renamed due to a schema version change.
170You can use the [`@SerialName` annotation](basic-serialization.md#serial-field-names) to change the name of a JSON field,
171but such renaming blocks the ability to decode data with the old name.
172To support multiple JSON names for the one Kotlin property, there is the [JsonNames] annotation:
173
174```kotlin
175@OptIn(ExperimentalSerializationApi::class) // JsonNames is an experimental annotation for now
176@Serializable
177data class Project(@JsonNames("title") val name: String)
178
179fun main() {
180  val project = Json.decodeFromString<Project>("""{"name":"kotlinx.serialization"}""")
181  println(project)
182  val oldProject = Json.decodeFromString<Project>("""{"title":"kotlinx.coroutines"}""")
183  println(oldProject)
184}
185```
186
187> You can get the full code [here](../guide/example/example-json-04.kt).
188
189As you can see, both `name` and `title` Json fields correspond to `name` property:
190
191```text
192Project(name=kotlinx.serialization)
193Project(name=kotlinx.coroutines)
194```
195
196Support for [JsonNames] annotation is controlled by the [JsonBuilder.useAlternativeNames] flag.
197Unlike most of the configuration flags, this one is enabled by default and does not need attention
198unless you want to do some fine-tuning.
199
200<!--- TEST -->
201
202### Encoding defaults
203
204Default values of properties are not encoded by default because they will be assigned to missing fields during decoding anyway.
205See the [Defaults are not encoded](basic-serialization.md#defaults-are-not-encoded-by-default) section for details and an example.
206This is especially useful for nullable properties with null defaults and avoids writing the corresponding null values.
207The default behavior can be changed by setting the [encodeDefaults][JsonBuilder.encodeDefaults] property to `true`:
208
209```kotlin
210val format = Json { encodeDefaults = true }
211
212@Serializable
213class Project(
214    val name: String,
215    val language: String = "Kotlin",
216    val website: String? = null
217)
218
219fun main() {
220    val data = Project("kotlinx.serialization")
221    println(format.encodeToString(data))
222}
223```
224
225> You can get the full code [here](../guide/example/example-json-05.kt).
226
227It produces the following output which encodes all the property values including the default ones:
228
229```text
230{"name":"kotlinx.serialization","language":"Kotlin","website":null}
231```
232
233<!--- TEST -->
234
235### Explicit nulls
236
237By default, all `null` values are encoded into JSON strings, but in some cases you may want to omit them.
238The encoding of `null` values can be controlled with the [explicitNulls][JsonBuilder.explicitNulls] property.
239
240If you set property to `false`, fields with `null` values are not encoded into JSON even if the property does not have a
241default `null` value. When decoding such JSON, the absence of a property value is treated as `null` for nullable properties
242without a default value.
243
244```kotlin
245val format = Json { explicitNulls = false }
246
247@Serializable
248data class Project(
249    val name: String,
250    val language: String,
251    val version: String? = "1.2.2",
252    val website: String?,
253    val description: String? = null
254)
255
256fun main() {
257    val data = Project("kotlinx.serialization", "Kotlin", null, null, null)
258    val json = format.encodeToString(data)
259    println(json)
260    println(format.decodeFromString<Project>(json))
261}
262```
263
264> You can get the full code [here](../guide/example/example-json-06.kt).
265
266As you can see, `version`, `website` and `description` fields are not present in output JSON on the first line.
267After decoding, the missing nullable property `website` without a default values has received a `null` value,
268while nullable properties `version` and `description` are filled with their default values:
269
270```text
271{"name":"kotlinx.serialization","language":"Kotlin"}
272Project(name=kotlinx.serialization, language=Kotlin, version=1.2.2, website=null, description=null)
273```
274
275> Pay attention to the fact that `version` was `null` before encoding and became `1.2.2` after decoding.
276> Encoding/decoding of properties like this — nullable with a non-null default — becomes asymmetrical if `explicitNulls` is set to `false`.
277
278It is possible to make the decoder treat some invalid input data as a missing field to enhance the functionality of this flag.
279See [coerceInputValues](#coercing-input-values) below for details.
280
281`explicitNulls` is `true` by default as it is the default behavior across different versions of the library.
282
283<!--- TEST -->
284
285### Coercing input values
286
287JSON formats that from third parties can evolve, sometimes changing the field types.
288This can lead to exceptions during decoding when the actual values do not match the expected values.
289The default [Json] implementation is strict with respect to input types as was demonstrated in
290the [Type safety is enforced](basic-serialization.md#type-safety-is-enforced) section. You can relax this restriction
291using the [coerceInputValues][JsonBuilder.coerceInputValues] property.
292
293This property only affects decoding. It treats a limited subset of invalid input values as if the
294corresponding property was missing.
295The current list of supported invalid values is:
296
297* `null` inputs for non-nullable types
298* unknown values for enums
299
300If value is missing, it is replaced either with a default property value if it exists,
301or with a `null` if [explicitNulls](#explicit-nulls) flag is set to `false` and a property is nullable (for enums).
302
303> This list may be expanded in the future, so that [Json] instance configured with this property becomes even more
304> permissive to invalid value in the input, replacing them with defaults or nulls.
305
306See the example from the [Type safety is enforced](basic-serialization.md#type-safety-is-enforced) section:
307
308```kotlin
309val format = Json { coerceInputValues = true }
310
311@Serializable
312data class Project(val name: String, val language: String = "Kotlin")
313
314fun main() {
315    val data = format.decodeFromString<Project>("""
316        {"name":"kotlinx.serialization","language":null}
317    """)
318    println(data)
319}
320```
321
322> You can get the full code [here](../guide/example/example-json-07.kt).
323
324The invalid `null` value for the `language` property was coerced into the default value:
325
326```text
327Project(name=kotlinx.serialization, language=Kotlin)
328```
329
330<!--- TEST -->
331
332Example of using this flag together with [explicitNulls](#explicit-nulls) to coerce invalid enum values:
333
334```kotlin
335enum class Color { BLACK, WHITE }
336
337@Serializable
338data class Brush(val foreground: Color = Color.BLACK, val background: Color?)
339
340val json = Json {
341  coerceInputValues = true
342  explicitNulls = false
343}
344
345fun main() {
346    val brush = json.decodeFromString<Brush>("""{"foreground":"pink", "background":"purple"}""")
347  println(brush)
348}
349```
350
351> You can get the full code [here](../guide/example/example-json-08.kt).
352
353Despite that we do not have `Color.pink` and `Color.purple` colors, `decodeFromString` function returns successfully:
354
355```text
356Brush(foreground=BLACK, background=null)
357```
358
359`foreground` property received its default value, and `background` property received `null` because of `explicitNulls = false` setting.
360
361<!--- TEST -->
362
363### Allowing structured map keys
364
365JSON format does not natively support the concept of a map with structured keys. Keys in JSON objects
366are strings and can be used to represent only primitives or enums by default.
367You can enable non-standard support for structured keys with
368the [allowStructuredMapKeys][JsonBuilder.allowStructuredMapKeys] property.
369
370This is how you can serialize a map with keys of a user-defined class:
371
372```kotlin
373val format = Json { allowStructuredMapKeys = true }
374
375@Serializable
376data class Project(val name: String)
377
378fun main() {
379    val map = mapOf(
380        Project("kotlinx.serialization") to "Serialization",
381        Project("kotlinx.coroutines") to "Coroutines"
382    )
383    println(format.encodeToString(map))
384}
385```
386
387> You can get the full code [here](../guide/example/example-json-09.kt).
388
389The map with structured keys gets represented as JSON array with the following items: `[key1, value1, key2, value2,...]`.
390
391```text
392[{"name":"kotlinx.serialization"},"Serialization",{"name":"kotlinx.coroutines"},"Coroutines"]
393```
394
395<!--- TEST -->
396
397### Allowing special floating-point values
398
399By default, special floating-point values like [Double.NaN] and infinities are not supported in JSON because
400the JSON specification prohibits it.
401You can enable their encoding using the [allowSpecialFloatingPointValues][JsonBuilder.allowSpecialFloatingPointValues]
402property:
403
404```kotlin
405val format = Json { allowSpecialFloatingPointValues = true }
406
407@Serializable
408class Data(
409    val value: Double
410)
411
412fun main() {
413    val data = Data(Double.NaN)
414    println(format.encodeToString(data))
415}
416```
417
418> You can get the full code [here](../guide/example/example-json-10.kt).
419
420This example produces the following non-stardard JSON output, yet it is a widely used encoding for
421special values in JVM world:
422
423```text
424{"value":NaN}
425```
426
427<!--- TEST -->
428
429### Class discriminator for polymorphism
430
431A key name that specifies a type when you have a polymorphic data can be specified
432in the [classDiscriminator][JsonBuilder.classDiscriminator] property:
433
434```kotlin
435val format = Json { classDiscriminator = "#class" }
436
437@Serializable
438sealed class Project {
439    abstract val name: String
440}
441
442@Serializable
443@SerialName("owned")
444class OwnedProject(override val name: String, val owner: String) : Project()
445
446fun main() {
447    val data: Project = OwnedProject("kotlinx.coroutines", "kotlin")
448    println(format.encodeToString(data))
449}
450```
451
452> You can get the full code [here](../guide/example/example-json-11.kt).
453
454In combination with an explicitly specified [SerialName] of the class it provides full
455control over the resulting JSON object:
456
457```text
458{"#class":"owned","name":"kotlinx.coroutines","owner":"kotlin"}
459```
460
461<!--- TEST -->
462
463It is also possible to specify different class discriminators for different hierarchies. Instead of Json instance property, use [JsonClassDiscriminator] annotation directly on base serializable class:
464
465```kotlin
466@OptIn(ExperimentalSerializationApi::class) // JsonClassDiscriminator is an experimental annotation for now
467@Serializable
468@JsonClassDiscriminator("message_type")
469sealed class Base
470```
471
472This annotation is _inheritable_, so all subclasses of `Base` will have the same discriminator:
473
474```kotlin
475@Serializable // Class discriminator is inherited from Base
476sealed class ErrorClass: Base()
477```
478
479> To learn more about inheritable serial annotations, see documentation for [InheritableSerialInfo].
480
481Note that it is not possible to explicitly specify different class discriminators in subclasses of `Base`. Only hierarchies with empty intersections can have different discriminators.
482
483Discriminator specified in the annotation has priority over discriminator in Json configuration:
484
485<!--- INCLUDE
486
487@Serializable
488data class Message(val message: Base, val error: ErrorClass?)
489
490@Serializable
491@SerialName("my.app.BaseMessage")
492data class BaseMessage(val message: String) : Base()
493
494@Serializable
495@SerialName("my.app.GenericError")
496data class GenericError(@SerialName("error_code") val errorCode: Int) : ErrorClass()
497-->
498
499```kotlin
500
501val format = Json { classDiscriminator = "#class" }
502
503fun main() {
504    val data = Message(BaseMessage("not found"), GenericError(404))
505    println(format.encodeToString(data))
506}
507```
508
509> You can get the full code [here](../guide/example/example-json-12.kt).
510
511As you can see, discriminator from the `Base` class is used:
512
513```text
514{"message":{"message_type":"my.app.BaseMessage","message":"not found"},"error":{"message_type":"my.app.GenericError","error_code":404}}
515```
516
517<!--- TEST -->
518
519### Class discriminator output mode
520
521Class discriminator provides information for serializing and deserializing [polymorphic class hierarchies](polymorphism.md#sealed-classes).
522As shown above, it is only added for polymorphic classes by default.
523In case you want to encode more or less information for various third party APIs about types in the output, it is possible to control
524addition of the class discriminator with the [JsonBuilder.classDiscriminatorMode] property.
525
526For example, [ClassDiscriminatorMode.NONE] does not add class discriminator at all, in case the receiving party is not interested in Kotlin types:
527
528```kotlin
529@OptIn(ExperimentalSerializationApi::class) // classDiscriminatorMode is an experimental setting for now
530val format = Json { classDiscriminatorMode = ClassDiscriminatorMode.NONE }
531
532@Serializable
533sealed class Project {
534    abstract val name: String
535}
536
537@Serializable
538class OwnedProject(override val name: String, val owner: String) : Project()
539
540fun main() {
541    val data: Project = OwnedProject("kotlinx.coroutines", "kotlin")
542    println(format.encodeToString(data))
543}
544```
545
546> You can get the full code [here](../guide/example/example-json-13.kt).
547
548Note that it would be impossible to deserialize this output back with kotlinx.serialization.
549
550```text
551{"name":"kotlinx.coroutines","owner":"kotlin"}
552```
553
554Two other available values are [ClassDiscriminatorMode.POLYMORPHIC] (default behavior) and [ClassDiscriminatorMode.ALL_JSON_OBJECTS] (adds discriminator whenever possible).
555Consult their documentation for details.
556
557<!--- TEST -->
558
559### Decoding enums in a case-insensitive manner
560
561[Kotlin's naming policy recommends](https://kotlinlang.org/docs/coding-conventions.html#property-names) naming enum values
562using either uppercase underscore-separated names or upper camel case names.
563[Json] uses exact Kotlin enum values names for decoding by default.
564However, sometimes third-party JSONs have such values named in lowercase or some mixed case.
565In this case, it is possible to decode enum values in a case-insensitive manner using [JsonBuilder.decodeEnumsCaseInsensitive] property:
566
567```kotlin
568@OptIn(ExperimentalSerializationApi::class) // decodeEnumsCaseInsensitive is an experimental setting for now
569val format = Json { decodeEnumsCaseInsensitive = true }
570
571@OptIn(ExperimentalSerializationApi::class) // JsonNames is an experimental annotation for now
572enum class Cases { VALUE_A, @JsonNames("Alternative") VALUE_B }
573
574@Serializable
575data class CasesList(val cases: List<Cases>)
576
577fun main() {
578  println(format.decodeFromString<CasesList>("""{"cases":["value_A", "alternative"]}"""))
579}
580```
581
582> You can get the full code [here](../guide/example/example-json-14.kt).
583
584It affects serial names as well as alternative names specified with [JsonNames] annotation, so both values are successfully decoded:
585
586```text
587CasesList(cases=[VALUE_A, VALUE_B])
588```
589
590This property does not affect encoding in any way.
591
592<!--- TEST -->
593
594### Global naming strategy
595
596If properties' names in Json input are different from Kotlin ones, it is recommended to specify the name
597for each property explicitly using [`@SerialName` annotation](basic-serialization.md#serial-field-names).
598However, there are certain situations where transformation should be applied to every serial name — such as migration
599from other frameworks or legacy codebase. For that cases, it is possible to specify a [namingStrategy][JsonBuilder.namingStrategy]
600for 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):
601
602```kotlin
603@Serializable
604data class Project(val projectName: String, val projectOwner: String)
605
606@OptIn(ExperimentalSerializationApi::class) // namingStrategy is an experimental setting for now
607val format = Json { namingStrategy = JsonNamingStrategy.SnakeCase }
608
609fun main() {
610    val project = format.decodeFromString<Project>("""{"project_name":"kotlinx.coroutines", "project_owner":"Kotlin"}""")
611    println(format.encodeToString(project.copy(projectName = "kotlinx.serialization")))
612}
613```
614
615> You can get the full code [here](../guide/example/example-json-15.kt).
616
617As you can see, both serialization and deserialization work as if all serial names are transformed from camel case to snake case:
618
619```text
620{"project_name":"kotlinx.serialization","project_owner":"Kotlin"}
621```
622
623There are some caveats one should remember while dealing with a [JsonNamingStrategy]:
624
625* Due to the nature of the `kotlinx.serialization` framework, naming strategy transformation is applied to all properties regardless
626of whether their serial name was taken from the property name or provided by [SerialName] annotation.
627Effectively, it means one cannot avoid transformation by explicitly specifying the serial name. To be able to deserialize
628non-transformed names, [JsonNames] annotation can be used instead.
629
630* Collision of the transformed name with any other (transformed) properties serial names or any alternative names
631specified with [JsonNames] will lead to a deserialization exception.
632
633* Global naming strategies are very implicit: by looking only at the definition of the class,
634it is impossible to determine which names it will have in the serialized form.
635As a consequence, naming strategies are not friendly to actions like Find Usages/Rename in IDE, full-text search by grep, etc.
636For them, the original name and the transformed are two different things;
637changing one without the other may introduce bugs in many unexpected ways and lead to greater maintenance efforts for code with global naming strategies.
638
639Therefore, one should carefully weigh the pros and cons before considering adding global naming strategies to an application.
640
641<!--- TEST -->
642
643### Base64
644
645To encode and decode Base64 formats, we will need to manually write a serializer. Here, we will use a default
646implementation of Kotlin's Base64 encoder. Note that some serializers use different RFCs for Base64 encoding by default.
647For example, Jackson uses a variant of [Base64 Mime](https://datatracker.ietf.org/doc/html/rfc2045). The same result in
648kotlinx.serialization can be achieved with Base64.Mime encoder.
649[Kotlin's documentation for Base64](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.io.encoding/-base64/) lists
650other available encoders.
651
652```kotlin
653import kotlinx.serialization.encoding.Encoder
654import kotlinx.serialization.encoding.Decoder
655import kotlinx.serialization.descriptors.*
656import kotlin.io.encoding.*
657
658@OptIn(ExperimentalEncodingApi::class)
659object ByteArrayAsBase64Serializer : KSerializer<ByteArray> {
660    private val base64 = Base64.Default
661
662    override val descriptor: SerialDescriptor
663        get() = PrimitiveSerialDescriptor(
664            "ByteArrayAsBase64Serializer",
665            PrimitiveKind.STRING
666        )
667
668    override fun serialize(encoder: Encoder, value: ByteArray) {
669        val base64Encoded = base64.encode(value)
670        encoder.encodeString(base64Encoded)
671    }
672
673    override fun deserialize(decoder: Decoder): ByteArray {
674        val base64Decoded = decoder.decodeString()
675        return base64.decode(base64Decoded)
676    }
677}
678```
679
680For more details on how to create your own custom serializer, you can
681see [custom serializers](serializers.md#custom-serializers).
682
683Then we can use it like this:
684
685```kotlin
686@Serializable
687data class Value(
688    @Serializable(with = ByteArrayAsBase64Serializer::class)
689    val base64Input: ByteArray
690) {
691    override fun equals(other: Any?): Boolean {
692        if (this === other) return true
693        if (javaClass != other?.javaClass) return false
694        other as Value
695        return base64Input.contentEquals(other.base64Input)
696    }
697
698    override fun hashCode(): Int {
699        return base64Input.contentHashCode()
700    }
701}
702
703fun main() {
704    val string = "foo string"
705    val value = Value(string.toByteArray())
706    val encoded = Json.encodeToString(value)
707    println(encoded)
708    val decoded = Json.decodeFromString<Value>(encoded)
709    println(decoded.base64Input.decodeToString())
710}
711```
712
713> You can get the full code [here](../guide/example/example-json-16.kt)
714
715```text
716{"base64Input":"Zm9vIHN0cmluZw=="}
717foo string
718```
719
720Notice the serializer we wrote is not dependent on `Json` format, therefore, it can be used in any format.
721
722For projects that use this serializer in many places, to avoid specifying the serializer every time, it is possible
723to [specify a serializer globally using typealias](serializers.md#specifying-serializer-globally-using-typealias).
724For example:
725````kotlin
726typealias Base64ByteArray = @Serializable(ByteArrayAsBase64Serializer::class) ByteArray
727````
728
729<!--- TEST -->
730
731## Json elements
732
733Aside from direct conversions between strings and JSON objects, Kotlin serialization offers APIs that allow
734other ways of working with JSON in the code. For example, you might need to tweak the data before it can parse
735or otherwise work with such an unstructured data that it does not readily fit into the typesafe world of Kotlin
736serialization.
737
738The main concept in this part of the library is [JsonElement]. Read on to learn what you can do with it.
739
740### Parsing to Json element
741
742A string can be _parsed_ into an instance of [JsonElement] with the [Json.parseToJsonElement] function.
743It is called neither decoding nor deserialization because none of that happens in the process.
744It just parses a JSON and forms an object representing it:
745
746```kotlin
747fun main() {
748    val element = Json.parseToJsonElement("""
749        {"name":"kotlinx.serialization","language":"Kotlin"}
750    """)
751    println(element)
752}
753```
754
755> You can get the full code [here](../guide/example/example-json-17.kt).
756
757A `JsonElement` prints itself as a valid JSON:
758
759```text
760{"name":"kotlinx.serialization","language":"Kotlin"}
761```
762
763<!--- TEST -->
764
765### Types of Json elements
766
767A [JsonElement] class has three direct subtypes, closely following JSON grammar:
768
769* [JsonPrimitive] represents primitive JSON elements, such as string, number, boolean, and null.
770  Each primitive has a simple string [content][JsonPrimitive.content]. There is also a
771  [JsonPrimitive()] constructor function overloaded to accept various primitive Kotlin types and
772  to convert them to `JsonPrimitive`.
773
774* [JsonArray] represents a JSON `[...]` array. It is a Kotlin [List] of `JsonElement` items.
775
776* [JsonObject] represents a JSON `{...}` object. It is a Kotlin [Map] from `String` keys to `JsonElement` values.
777
778The `JsonElement` class has extensions that cast it to its corresponding subtypes:
779[jsonPrimitive][_jsonPrimitive], [jsonArray][_jsonArray], [jsonObject][_jsonObject]. The `JsonPrimitive` class,
780in turn, provides converters to Kotlin primitive types: [int], [intOrNull], [long], [longOrNull],
781and similar ones for other types. This is how you can use them for processing JSON whose structure you know:
782
783```kotlin
784fun main() {
785    val element = Json.parseToJsonElement("""
786        {
787            "name": "kotlinx.serialization",
788            "forks": [{"votes": 42}, {"votes": 9000}, {}]
789        }
790    """)
791    val sum = element
792        .jsonObject["forks"]!!
793        .jsonArray.sumOf { it.jsonObject["votes"]?.jsonPrimitive?.int ?: 0 }
794    println(sum)
795}
796```
797
798> You can get the full code [here](../guide/example/example-json-18.kt).
799
800The above example sums `votes` in all objects in the `forks` array, ignoring the objects that have no `votes`:
801
802```text
8039042
804```
805
806<!--- TEST -->
807
808Note that the execution will fail if the structure of the data is otherwise different.
809
810### Json element builders
811
812You can construct instances of specific [JsonElement] subtypes using the respective builder functions
813[buildJsonArray] and [buildJsonObject]. They provide a DSL to define the resulting JSON structure. It
814is similar to Kotlin standard library collection builders, but with a JSON-specific convenience
815of more type-specific overloads and inner builder functions. The following example shows
816all the key features:
817
818```kotlin
819fun main() {
820    val element = buildJsonObject {
821        put("name", "kotlinx.serialization")
822        putJsonObject("owner") {
823            put("name", "kotlin")
824        }
825        putJsonArray("forks") {
826            addJsonObject {
827                put("votes", 42)
828            }
829            addJsonObject {
830                put("votes", 9000)
831            }
832        }
833    }
834    println(element)
835}
836```
837
838> You can get the full code [here](../guide/example/example-json-19.kt).
839
840As a result, you get a proper JSON string:
841
842```text
843{"name":"kotlinx.serialization","owner":{"name":"kotlin"},"forks":[{"votes":42},{"votes":9000}]}
844```
845
846<!--- TEST -->
847
848### Decoding Json elements
849
850An instance of the [JsonElement] class can be decoded into a serializable object using
851the [Json.decodeFromJsonElement] function:
852
853```kotlin
854@Serializable
855data class Project(val name: String, val language: String)
856
857fun main() {
858    val element = buildJsonObject {
859        put("name", "kotlinx.serialization")
860        put("language", "Kotlin")
861    }
862    val data = Json.decodeFromJsonElement<Project>(element)
863    println(data)
864}
865```
866
867> You can get the full code [here](../guide/example/example-json-20.kt).
868
869The result is exactly what you would expect:
870
871```text
872Project(name=kotlinx.serialization, language=Kotlin)
873```
874
875<!--- TEST -->
876
877### Encoding literal Json content (experimental)
878
879> This functionality is experimental and requires opting-in to [the experimental Kotlinx Serialization API](compatibility.md#experimental-api).
880
881In some cases it might be necessary to encode an arbitrary unquoted value.
882This can be achieved with [JsonUnquotedLiteral].
883
884#### Serializing large decimal numbers
885
886The JSON specification does not restrict the size or precision of numbers, however it is not possible to serialize
887numbers of arbitrary size or precision using [JsonPrimitive()].
888
889If [Double] is used, then the numbers are limited in precision, meaning that large numbers are truncated.
890When using Kotlin/JVM [BigDecimal] can be used instead, but [JsonPrimitive()] will encode the value as a string, not a
891number.
892
893```kotlin
894import java.math.BigDecimal
895
896val format = Json { prettyPrint = true }
897
898fun main() {
899    val pi = BigDecimal("3.141592653589793238462643383279")
900
901    val piJsonDouble = JsonPrimitive(pi.toDouble())
902    val piJsonString = JsonPrimitive(pi.toString())
903
904    val piObject = buildJsonObject {
905        put("pi_double", piJsonDouble)
906        put("pi_string", piJsonString)
907    }
908
909    println(format.encodeToString(piObject))
910}
911```
912
913> You can get the full code [here](../guide/example/example-json-21.kt).
914
915Even though `pi` was defined as a number with 30 decimal places, the resulting JSON does not reflect this.
916The [Double] value is truncated to 15 decimal places, and the String is wrapped in quotes - which is not a JSON number.
917
918```text
919{
920    "pi_double": 3.141592653589793,
921    "pi_string": "3.141592653589793238462643383279"
922}
923```
924
925<!--- TEST -->
926
927To avoid precision loss, the string value of `pi` can be encoded using [JsonUnquotedLiteral].
928
929```kotlin
930import java.math.BigDecimal
931
932val format = Json { prettyPrint = true }
933
934fun main() {
935    val pi = BigDecimal("3.141592653589793238462643383279")
936
937    // use JsonUnquotedLiteral to encode raw JSON content
938    @OptIn(ExperimentalSerializationApi::class)
939    val piJsonLiteral = JsonUnquotedLiteral(pi.toString())
940
941    val piJsonDouble = JsonPrimitive(pi.toDouble())
942    val piJsonString = JsonPrimitive(pi.toString())
943
944    val piObject = buildJsonObject {
945        put("pi_literal", piJsonLiteral)
946        put("pi_double", piJsonDouble)
947        put("pi_string", piJsonString)
948    }
949
950    println(format.encodeToString(piObject))
951}
952```
953
954> You can get the full code [here](../guide/example/example-json-22.kt).
955
956`pi_literal` now accurately matches the value defined.
957
958```text
959{
960    "pi_literal": 3.141592653589793238462643383279,
961    "pi_double": 3.141592653589793,
962    "pi_string": "3.141592653589793238462643383279"
963}
964```
965
966<!--- TEST -->
967
968To decode `pi` back to a [BigDecimal], the string content of the [JsonPrimitive] can be used.
969
970(This demonstration uses a [JsonPrimitive] for simplicity. For a more re-usable method of handling serialization, see
971[Json Transformations](#json-transformations) below.)
972
973
974```kotlin
975import java.math.BigDecimal
976
977fun main() {
978    val piObjectJson = """
979          {
980              "pi_literal": 3.141592653589793238462643383279
981          }
982      """.trimIndent()
983
984    val piObject: JsonObject = Json.decodeFromString(piObjectJson)
985
986    val piJsonLiteral = piObject["pi_literal"]!!.jsonPrimitive.content
987
988    val pi = BigDecimal(piJsonLiteral)
989
990    println(pi)
991}
992```
993
994> You can get the full code [here](../guide/example/example-json-23.kt).
995
996The exact value of `pi` is decoded, with all 30 decimal places of precision that were in the source JSON.
997
998```text
9993.141592653589793238462643383279
1000```
1001
1002<!--- TEST -->
1003
1004#### Using `JsonUnquotedLiteral` to create a literal unquoted value of `null` is forbidden
1005
1006To avoid creating an inconsistent state, encoding a String equal to `"null"` is forbidden.
1007Use [JsonNull] or [JsonPrimitive] instead.
1008
1009```kotlin
1010@OptIn(ExperimentalSerializationApi::class)
1011fun main() {
1012    // caution: creating null with JsonUnquotedLiteral will cause an exception!
1013    JsonUnquotedLiteral("null")
1014}
1015```
1016
1017> You can get the full code [here](../guide/example/example-json-24.kt).
1018
1019```text
1020Exception 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
1021```
1022
1023<!--- TEST LINES_START -->
1024
1025
1026## Json transformations
1027
1028To affect the shape and contents of JSON output after serialization, or adapt input to deserialization,
1029it is possible to write a [custom serializer](serializers.md). However, it may be inconvenient to
1030carefully follow [Encoder] and [Decoder] calling conventions, especially for relatively small and easy tasks.
1031For that purpose, Kotlin serialization provides an API that can reduce the burden of implementing a custom
1032serializer to a problem of manipulating a Json elements tree.
1033
1034We recommend that you get familiar with the [Serializers](serializers.md) chapter: among other things, it
1035explains how custom serializers are bound to classes.
1036
1037Transformation capabilities are provided by the abstract [JsonTransformingSerializer] class which implements [KSerializer].
1038Instead of direct interaction with `Encoder` or `Decoder`, this class asks you to supply transformations for JSON tree
1039represented by the [JsonElement] class using the`transformSerialize` and
1040`transformDeserialize` methods. Let's take a look at the examples.
1041
1042### Array wrapping
1043
1044The first example is an implementation of JSON array wrapping for lists.
1045
1046Consider a REST API that returns a JSON array of `User` objects, or a single object (not wrapped into an array) if there
1047is only one element in the result.
1048
1049In the data model, use the [`@Serializable`][Serializable] annotation to specify a custom serializer for a
1050`users: List<User>` property.
1051
1052<!--- INCLUDE
1053import kotlinx.serialization.builtins.*
1054-->
1055
1056```kotlin
1057@Serializable
1058data class Project(
1059    val name: String,
1060    @Serializable(with = UserListSerializer::class)
1061    val users: List<User>
1062)
1063
1064@Serializable
1065data class User(val name: String)
1066```
1067
1068Since this example covers only the deserialization case, you can implement `UserListSerializer` and override only the
1069`transformDeserialize` function. The `JsonTransformingSerializer` constructor takes an original serializer
1070as parameter (this approach is shown in the section [Constructing collection serializers](serializers.md#constructing-collection-serializers)):
1071
1072```kotlin
1073object UserListSerializer : JsonTransformingSerializer<List<User>>(ListSerializer(User.serializer())) {
1074    // If response is not an array, then it is a single object that should be wrapped into the array
1075    override fun transformDeserialize(element: JsonElement): JsonElement =
1076        if (element !is JsonArray) JsonArray(listOf(element)) else element
1077}
1078```
1079
1080Now you can test the code with a JSON array or a single JSON object as inputs.
1081
1082```kotlin
1083fun main() {
1084    println(Json.decodeFromString<Project>("""
1085        {"name":"kotlinx.serialization","users":{"name":"kotlin"}}
1086    """))
1087    println(Json.decodeFromString<Project>("""
1088        {"name":"kotlinx.serialization","users":[{"name":"kotlin"},{"name":"jetbrains"}]}
1089    """))
1090}
1091```
1092
1093> You can get the full code [here](../guide/example/example-json-25.kt).
1094
1095The output shows that both cases are correctly deserialized into a Kotlin [List].
1096
1097```text
1098Project(name=kotlinx.serialization, users=[User(name=kotlin)])
1099Project(name=kotlinx.serialization, users=[User(name=kotlin), User(name=jetbrains)])
1100```
1101
1102<!--- TEST -->
1103
1104### Array unwrapping
1105
1106You can also implement the `transformSerialize` function to unwrap a single-element list into a single JSON object
1107during serialization:
1108
1109<!--- INCLUDE
1110import kotlinx.serialization.builtins.*
1111
1112@Serializable
1113data class Project(
1114    val name: String,
1115    @Serializable(with = UserListSerializer::class)
1116    val users: List<User>
1117)
1118
1119@Serializable
1120data class User(val name: String)
1121
1122object UserListSerializer : JsonTransformingSerializer<List<User>>(ListSerializer(User.serializer())) {
1123-->
1124
1125```kotlin
1126    override fun transformSerialize(element: JsonElement): JsonElement {
1127        require(element is JsonArray) // this serializer is used only with lists
1128        return element.singleOrNull() ?: element
1129    }
1130```
1131
1132<!--- INCLUDE
1133}
1134-->
1135
1136Now, if you serialize a single-element list of objects from Kotlin:
1137
1138```kotlin
1139fun main() {
1140    val data = Project("kotlinx.serialization", listOf(User("kotlin")))
1141    println(Json.encodeToString(data))
1142}
1143```
1144
1145> You can get the full code [here](../guide/example/example-json-26.kt).
1146
1147You end up with a single JSON object, not an array with one element:
1148
1149```text
1150{"name":"kotlinx.serialization","users":{"name":"kotlin"}}
1151```
1152
1153<!--- TEST -->
1154
1155### Manipulating default values
1156
1157Another kind of useful transformation is omitting specific values from the output JSON, for example, if it
1158is used as default when missing or for other reasons.
1159
1160Imagine that you cannot specify a default value for the `language` property in the `Project` data model for some reason,
1161but you need it omitted from the JSON when it is equal to `Kotlin` (we can all agree that Kotlin should be default anyway).
1162You can fix it by writing the special `ProjectSerializer` based on
1163the [Plugin-generated serializer](serializers.md#plugin-generated-serializer) for the `Project` class.
1164
1165```kotlin
1166@Serializable
1167class Project(val name: String, val language: String)
1168
1169object ProjectSerializer : JsonTransformingSerializer<Project>(Project.serializer()) {
1170    override fun transformSerialize(element: JsonElement): JsonElement =
1171        // Filter out top-level key value pair with the key "language" and the value "Kotlin"
1172        JsonObject(element.jsonObject.filterNot {
1173            (k, v) -> k == "language" && v.jsonPrimitive.content == "Kotlin"
1174        })
1175}
1176```
1177
1178In the example below, we are serializing the `Project` class at the top-level, so we explicitly
1179pass the above `ProjectSerializer` to [Json.encodeToString] function as was shown in
1180the [Passing a serializer manually](serializers.md#passing-a-serializer-manually) section:
1181
1182```kotlin
1183fun main() {
1184    val data = Project("kotlinx.serialization", "Kotlin")
1185    println(Json.encodeToString(data)) // using plugin-generated serializer
1186    println(Json.encodeToString(ProjectSerializer, data)) // using custom serializer
1187}
1188```
1189
1190> You can get the full code [here](../guide/example/example-json-27.kt).
1191
1192See the effect of the custom serializer:
1193
1194```text
1195{"name":"kotlinx.serialization","language":"Kotlin"}
1196{"name":"kotlinx.serialization"}
1197```
1198
1199<!--- TEST -->
1200
1201### Content-based polymorphic deserialization
1202
1203Typically, [polymorphic serialization](polymorphism.md) requires a dedicated `"type"` key
1204(also known as _class discriminator_) in the incoming JSON object to determine the actual serializer
1205which should be used to deserialize Kotlin class.
1206
1207However, sometimes the `type` property may not be present in the input. In this case, you need to guess
1208the actual type by the shape of JSON, for example by the presence of a specific key.
1209
1210[JsonContentPolymorphicSerializer] provides a skeleton implementation for such a strategy.
1211To use it, override its `selectDeserializer` method.
1212Let's start with the following class hierarchy.
1213
1214> Note that is does not have to be `sealed` as recommended in the [Sealed classes](polymorphism.md#sealed-classes) section,
1215> because we are not going to take advantage of the plugin-generated code that automatically selects the
1216> appropriate subclass, but are going to implement this code manually.
1217
1218<!--- INCLUDE
1219import kotlinx.serialization.builtins.*
1220-->
1221
1222```kotlin
1223@Serializable
1224abstract class Project {
1225    abstract val name: String
1226}
1227
1228@Serializable
1229data class BasicProject(override val name: String): Project()
1230
1231
1232@Serializable
1233data class OwnedProject(override val name: String, val owner: String) : Project()
1234```
1235
1236You can distinguish the `BasicProject` and `OwnedProject` subclasses by the presence of
1237the `owner` key in the JSON object.
1238
1239```kotlin
1240object ProjectSerializer : JsonContentPolymorphicSerializer<Project>(Project::class) {
1241    override fun selectDeserializer(element: JsonElement) = when {
1242        "owner" in element.jsonObject -> OwnedProject.serializer()
1243        else -> BasicProject.serializer()
1244    }
1245}
1246```
1247
1248When you use this serializer to serialize data, either [registered](polymorphism.md#registered-subclasses) or
1249the default serializer is selected for the actual type at runtime:
1250
1251```kotlin
1252fun main() {
1253    val data = listOf(
1254        OwnedProject("kotlinx.serialization", "kotlin"),
1255        BasicProject("example")
1256    )
1257    val string = Json.encodeToString(ListSerializer(ProjectSerializer), data)
1258    println(string)
1259    println(Json.decodeFromString(ListSerializer(ProjectSerializer), string))
1260}
1261```
1262
1263> You can get the full code [here](../guide/example/example-json-28.kt).
1264
1265No class discriminator is added in the JSON output:
1266
1267```text
1268[{"name":"kotlinx.serialization","owner":"kotlin"},{"name":"example"}]
1269[OwnedProject(name=kotlinx.serialization, owner=kotlin), BasicProject(name=example)]
1270```
1271
1272<!--- TEST -->
1273
1274### Extending the behavior of the plugin generated serializer
1275In some cases, it may be necessary to add additional serialization logic on top of the plugin generated logic.
1276For example, to add a preliminary modification of JSON elements or to add processing of unknown values of enums.
1277
1278In this case, you can mark the serializable class with the [`@KeepGeneratedSerializer`][KeepGeneratedSerializer] annotation and get the generated serializer using the `generatedSerializer()` function.
1279
1280> This annotation is currently experimental. Kotlin 2.0.20 or higher is required for this feature to work.
1281
1282Here is an example of the simultaneous use of [JsonTransformingSerializer] and polymorphism.
1283In this example, we use `transformDeserialize` function to rename `basic-name` key into `name` so it matches the `abstract val name` property from the `Project` supertype.
1284```kotlin
1285@Serializable
1286sealed class Project {
1287    abstract val name: String
1288}
1289
1290@OptIn(ExperimentalSerializationApi::class)
1291@KeepGeneratedSerializer
1292@Serializable(with = BasicProjectSerializer::class)
1293@SerialName("basic")
1294data class BasicProject(override val name: String): Project()
1295
1296object BasicProjectSerializer : JsonTransformingSerializer<BasicProject>(BasicProject.generatedSerializer()) {
1297    override fun transformDeserialize(element: JsonElement): JsonElement {
1298        val jsonObject = element.jsonObject
1299        return if ("basic-name" in jsonObject) {
1300            val nameElement = jsonObject["basic-name"] ?: throw IllegalStateException()
1301            JsonObject(mapOf("name" to nameElement))
1302        } else {
1303            jsonObject
1304        }
1305    }
1306}
1307
1308
1309fun main() {
1310    val project = Json.decodeFromString<Project>("""{"type":"basic","basic-name":"example"}""")
1311    println(project)
1312}
1313```
1314
1315> You can get the full code [here](../guide/example/example-json-29.kt).
1316
1317`BasicProject` will be printed to the output:
1318
1319```text
1320BasicProject(name=example)
1321```
1322<!--- TEST -->
1323
1324### Under the hood (experimental)
1325
1326Although abstract serializers mentioned above can cover most of the cases, it is possible to implement similar machinery
1327manually, using only the [KSerializer] class.
1328If tweaking the abstract methods `transformSerialize`/`transformDeserialize`/`selectDeserializer` is not enough,
1329then altering `serialize`/`deserialize` is a way to go.
1330
1331Here are some useful things about custom serializers with [Json]:
1332
1333* [Encoder] can be cast to [JsonEncoder], and [Decoder] to [JsonDecoder], if the current format is [Json].
1334* `JsonDecoder` has the [decodeJsonElement][JsonDecoder.decodeJsonElement] method and `JsonEncoder`
1335  has the [encodeJsonElement][JsonEncoder.encodeJsonElement] method,
1336  which basically retrieve an element from and insert an element to a current position in the stream.
1337* Both [`JsonDecoder`][JsonDecoder.json] and [`JsonEncoder`][JsonEncoder.json] have the `json` property,
1338  which returns [Json] instance with all settings that are currently in use.
1339* [Json] has the [encodeToJsonElement][Json.encodeToJsonElement] and [decodeFromJsonElement][Json.decodeFromJsonElement] methods.
1340
1341Given all that, it is possible to implement two-stage conversion `Decoder -> JsonElement -> value` or
1342`value -> JsonElement -> Encoder`.
1343For example, you can implement a fully custom serializer for the following `Response` class so that its
1344`Ok` subclass is represented directly, but the `Error` subclass is represented by an object with the error message:
1345
1346<!--- INCLUDE
1347import kotlinx.serialization.descriptors.*
1348import kotlinx.serialization.encoding.*
1349-->
1350
1351```kotlin
1352@Serializable(with = ResponseSerializer::class)
1353sealed class Response<out T> {
1354    data class Ok<out T>(val data: T) : Response<T>()
1355    data class Error(val message: String) : Response<Nothing>()
1356}
1357
1358class ResponseSerializer<T>(private val dataSerializer: KSerializer<T>) : KSerializer<Response<T>> {
1359    override val descriptor: SerialDescriptor = buildClassSerialDescriptor("Response") {
1360        element("Ok", dataSerializer.descriptor)
1361        element("Error", buildClassSerialDescriptor("Error") {
1362          element<String>("message")
1363        })
1364    }
1365
1366    override fun deserialize(decoder: Decoder): Response<T> {
1367        // Decoder -> JsonDecoder
1368        require(decoder is JsonDecoder) // this class can be decoded only by Json
1369        // JsonDecoder -> JsonElement
1370        val element = decoder.decodeJsonElement()
1371        // JsonElement -> value
1372        if (element is JsonObject && "error" in element)
1373            return Response.Error(element["error"]!!.jsonPrimitive.content)
1374        return Response.Ok(decoder.json.decodeFromJsonElement(dataSerializer, element))
1375    }
1376
1377    override fun serialize(encoder: Encoder, value: Response<T>) {
1378        // Encoder -> JsonEncoder
1379        require(encoder is JsonEncoder) // This class can be encoded only by Json
1380        // value -> JsonElement
1381        val element = when (value) {
1382            is Response.Ok -> encoder.json.encodeToJsonElement(dataSerializer, value.data)
1383            is Response.Error -> buildJsonObject { put("error", value.message) }
1384        }
1385        // JsonElement -> JsonEncoder
1386        encoder.encodeJsonElement(element)
1387    }
1388}
1389```
1390
1391Having this serializable `Response` implementation, you can take any serializable payload for its data
1392and serialize or deserialize the corresponding responses:
1393
1394```kotlin
1395@Serializable
1396data class Project(val name: String)
1397
1398fun main() {
1399    val responses = listOf(
1400        Response.Ok(Project("kotlinx.serialization")),
1401        Response.Error("Not found")
1402    )
1403    val string = Json.encodeToString(responses)
1404    println(string)
1405    println(Json.decodeFromString<List<Response<Project>>>(string))
1406}
1407```
1408
1409> You can get the full code [here](../guide/example/example-json-30.kt).
1410
1411This gives you fine-grained control on the representation of the `Response` class in the JSON output:
1412
1413```text
1414[{"name":"kotlinx.serialization"},{"error":"Not found"}]
1415[Ok(data=Project(name=kotlinx.serialization)), Error(message=Not found)]
1416```
1417
1418<!--- TEST -->
1419
1420### Maintaining custom JSON attributes
1421
1422A good example of custom JSON-specific serializer would be a deserializer
1423that packs all unknown JSON properties into a dedicated field of `JsonObject` type.
1424
1425Let's add `UnknownProject` &ndash; a class with the `name` property and arbitrary details flattened into the same object:
1426
1427<!--- INCLUDE
1428import kotlinx.serialization.descriptors.*
1429import kotlinx.serialization.encoding.*
1430-->
1431
1432```kotlin
1433data class UnknownProject(val name: String, val details: JsonObject)
1434```
1435
1436However, the default plugin-generated serializer requires details
1437to be a separate JSON object and that's not what we want.
1438
1439To mitigate that, write an own serializer that uses the fact that it works only with the `Json` format:
1440
1441```kotlin
1442object UnknownProjectSerializer : KSerializer<UnknownProject> {
1443    override val descriptor: SerialDescriptor = buildClassSerialDescriptor("UnknownProject") {
1444        element<String>("name")
1445        element<JsonElement>("details")
1446    }
1447
1448    override fun deserialize(decoder: Decoder): UnknownProject {
1449        // Cast to JSON-specific interface
1450        val jsonInput = decoder as? JsonDecoder ?: error("Can be deserialized only by JSON")
1451        // Read the whole content as JSON
1452        val json = jsonInput.decodeJsonElement().jsonObject
1453        // Extract and remove name property
1454        val name = json.getValue("name").jsonPrimitive.content
1455        val details = json.toMutableMap()
1456        details.remove("name")
1457        return UnknownProject(name, JsonObject(details))
1458    }
1459
1460    override fun serialize(encoder: Encoder, value: UnknownProject) {
1461        error("Serialization is not supported")
1462    }
1463}
1464```
1465
1466Now it can be used to read flattened JSON details as `UnknownProject`:
1467
1468```kotlin
1469fun main() {
1470    println(Json.decodeFromString(UnknownProjectSerializer, """{"type":"unknown","name":"example","maintainer":"Unknown","license":"Apache 2.0"}"""))
1471}
1472```
1473
1474> You can get the full code [here](../guide/example/example-json-31.kt).
1475
1476```text
1477UnknownProject(name=example, details={"type":"unknown","maintainer":"Unknown","license":"Apache 2.0"})
1478```
1479
1480<!--- TEST -->
1481
1482---
1483
1484The next chapter covers [Alternative and custom formats (experimental)](formats.md).
1485
1486
1487<!-- references -->
1488[RFC-4627]: https://www.ietf.org/rfc/rfc4627.txt
1489[BigDecimal]: https://docs.oracle.com/javase/8/docs/api/java/math/BigDecimal.html
1490
1491<!-- stdlib references -->
1492[Double]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-double/
1493[Double.NaN]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-double/-na-n.html
1494[List]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-list/
1495[Map]: https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/-map/
1496
1497<!--- MODULE /kotlinx-serialization-core -->
1498<!--- INDEX kotlinx-serialization-core/kotlinx.serialization -->
1499
1500[SerialName]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serial-name/index.html
1501[InheritableSerialInfo]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-inheritable-serial-info/index.html
1502[KSerializer]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-k-serializer/index.html
1503[Serializable]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serializable/index.html
1504[KeepGeneratedSerializer]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-keep-generated-serializer/index.html
1505
1506<!--- INDEX kotlinx-serialization-core/kotlinx.serialization.encoding -->
1507
1508[Encoder]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/index.html
1509[Decoder]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-decoder/index.html
1510
1511<!--- MODULE /kotlinx-serialization-json -->
1512<!--- INDEX kotlinx-serialization-json/kotlinx.serialization.json -->
1513
1514[Json]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/index.html
1515[Json()]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json.html
1516[JsonBuilder]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/index.html
1517[JsonBuilder.prettyPrint]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/pretty-print.html
1518[JsonBuilder.isLenient]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/is-lenient.html
1519[JsonBuilder.ignoreUnknownKeys]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/ignore-unknown-keys.html
1520[JsonNames]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-names/index.html
1521[JsonBuilder.useAlternativeNames]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/use-alternative-names.html
1522[JsonBuilder.encodeDefaults]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/encode-defaults.html
1523[JsonBuilder.explicitNulls]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/explicit-nulls.html
1524[JsonBuilder.coerceInputValues]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/coerce-input-values.html
1525[JsonBuilder.allowStructuredMapKeys]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/allow-structured-map-keys.html
1526[JsonBuilder.allowSpecialFloatingPointValues]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/allow-special-floating-point-values.html
1527[JsonBuilder.classDiscriminator]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/class-discriminator.html
1528[JsonClassDiscriminator]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-class-discriminator/index.html
1529[JsonBuilder.classDiscriminatorMode]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/class-discriminator-mode.html
1530[ClassDiscriminatorMode.NONE]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-class-discriminator-mode/-n-o-n-e/index.html
1531[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
1532[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
1533[JsonBuilder.decodeEnumsCaseInsensitive]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/decode-enums-case-insensitive.html
1534[JsonBuilder.namingStrategy]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/naming-strategy.html
1535[JsonNamingStrategy]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-naming-strategy/index.html
1536[JsonElement]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-element/index.html
1537[Json.parseToJsonElement]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/parse-to-json-element.html
1538[JsonPrimitive]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-primitive/index.html
1539[JsonPrimitive.content]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-primitive/content.html
1540[JsonPrimitive()]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-primitive.html
1541[JsonArray]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-array/index.html
1542[JsonObject]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-object/index.html
1543[_jsonPrimitive]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/json-primitive.html
1544[_jsonArray]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/json-array.html
1545[_jsonObject]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/json-object.html
1546[int]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/int.html
1547[intOrNull]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/int-or-null.html
1548[long]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/long.html
1549[longOrNull]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/long-or-null.html
1550[buildJsonArray]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/build-json-array.html
1551[buildJsonObject]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/build-json-object.html
1552[Json.decodeFromJsonElement]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/decode-from-json-element.html
1553[JsonUnquotedLiteral]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-unquoted-literal.html
1554[JsonNull]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-null/index.html
1555[JsonTransformingSerializer]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-transforming-serializer/index.html
1556[Json.encodeToString]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/encode-to-string.html
1557[JsonContentPolymorphicSerializer]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-content-polymorphic-serializer/index.html
1558[JsonEncoder]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-encoder/index.html
1559[JsonDecoder]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-decoder/index.html
1560[JsonDecoder.decodeJsonElement]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-decoder/decode-json-element.html
1561[JsonEncoder.encodeJsonElement]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-encoder/encode-json-element.html
1562[JsonDecoder.json]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-decoder/json.html
1563[JsonEncoder.json]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-encoder/json.html
1564[Json.encodeToJsonElement]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/encode-to-json-element.html
1565
1566<!--- END -->
1567