1*57b5a4a6SAndroid Build Coastguard Worker<!--- TEST_NAME SerializersTest --> 2*57b5a4a6SAndroid Build Coastguard Worker 3*57b5a4a6SAndroid Build Coastguard Worker# Serializers 4*57b5a4a6SAndroid Build Coastguard Worker 5*57b5a4a6SAndroid Build Coastguard WorkerThis is the third chapter of the [Kotlin Serialization Guide](serialization-guide.md). 6*57b5a4a6SAndroid Build Coastguard WorkerIn this chapter we'll take a look at serializers in more detail, and we'll see how custom serializers can be written. 7*57b5a4a6SAndroid Build Coastguard Worker 8*57b5a4a6SAndroid Build Coastguard Worker**Table of contents** 9*57b5a4a6SAndroid Build Coastguard Worker 10*57b5a4a6SAndroid Build Coastguard Worker<!--- TOC --> 11*57b5a4a6SAndroid Build Coastguard Worker 12*57b5a4a6SAndroid Build Coastguard Worker* [Introduction to serializers](#introduction-to-serializers) 13*57b5a4a6SAndroid Build Coastguard Worker * [Plugin-generated serializer](#plugin-generated-serializer) 14*57b5a4a6SAndroid Build Coastguard Worker * [Plugin-generated generic serializer](#plugin-generated-generic-serializer) 15*57b5a4a6SAndroid Build Coastguard Worker * [Builtin primitive serializers](#builtin-primitive-serializers) 16*57b5a4a6SAndroid Build Coastguard Worker * [Constructing collection serializers](#constructing-collection-serializers) 17*57b5a4a6SAndroid Build Coastguard Worker * [Using top-level serializer function](#using-top-level-serializer-function) 18*57b5a4a6SAndroid Build Coastguard Worker* [Custom serializers](#custom-serializers) 19*57b5a4a6SAndroid Build Coastguard Worker * [Primitive serializer](#primitive-serializer) 20*57b5a4a6SAndroid Build Coastguard Worker * [Delegating serializers](#delegating-serializers) 21*57b5a4a6SAndroid Build Coastguard Worker * [Composite serializer via surrogate](#composite-serializer-via-surrogate) 22*57b5a4a6SAndroid Build Coastguard Worker * [Hand-written composite serializer](#hand-written-composite-serializer) 23*57b5a4a6SAndroid Build Coastguard Worker * [Sequential decoding protocol (experimental)](#sequential-decoding-protocol-experimental) 24*57b5a4a6SAndroid Build Coastguard Worker * [Serializing 3rd party classes](#serializing-3rd-party-classes) 25*57b5a4a6SAndroid Build Coastguard Worker * [Passing a serializer manually](#passing-a-serializer-manually) 26*57b5a4a6SAndroid Build Coastguard Worker * [Specifying serializer on a property](#specifying-serializer-on-a-property) 27*57b5a4a6SAndroid Build Coastguard Worker * [Specifying serializer for a particular type](#specifying-serializer-for-a-particular-type) 28*57b5a4a6SAndroid Build Coastguard Worker * [Specifying serializers for a file](#specifying-serializers-for-a-file) 29*57b5a4a6SAndroid Build Coastguard Worker * [Specifying serializer globally using typealias](#specifying-serializer-globally-using-typealias) 30*57b5a4a6SAndroid Build Coastguard Worker * [Custom serializers for a generic type](#custom-serializers-for-a-generic-type) 31*57b5a4a6SAndroid Build Coastguard Worker * [Format-specific serializers](#format-specific-serializers) 32*57b5a4a6SAndroid Build Coastguard Worker* [Contextual serialization](#contextual-serialization) 33*57b5a4a6SAndroid Build Coastguard Worker * [Serializers module](#serializers-module) 34*57b5a4a6SAndroid Build Coastguard Worker * [Contextual serialization and generic classes](#contextual-serialization-and-generic-classes) 35*57b5a4a6SAndroid Build Coastguard Worker* [Deriving external serializer for another Kotlin class (experimental)](#deriving-external-serializer-for-another-kotlin-class-experimental) 36*57b5a4a6SAndroid Build Coastguard Worker * [External serialization uses properties](#external-serialization-uses-properties) 37*57b5a4a6SAndroid Build Coastguard Worker 38*57b5a4a6SAndroid Build Coastguard Worker<!--- END --> 39*57b5a4a6SAndroid Build Coastguard Worker 40*57b5a4a6SAndroid Build Coastguard Worker## Introduction to serializers 41*57b5a4a6SAndroid Build Coastguard Worker 42*57b5a4a6SAndroid Build Coastguard WorkerFormats, like JSON, control the _encoding_ of an object into specific output bytes, but how the object is decomposed 43*57b5a4a6SAndroid Build Coastguard Workerinto its constituent properties is controlled by a _serializer_. So far we've been using automatically-derived 44*57b5a4a6SAndroid Build Coastguard Workerserializers by using the [`@Serializable`][Serializable] annotation as explained in 45*57b5a4a6SAndroid Build Coastguard Workerthe [Serializable classes](/docs/basic-serialization.md#serializable-classes) section, or using builtin serializers that were shown in 46*57b5a4a6SAndroid Build Coastguard Workerthe [Builtin classes](/docs/builtin-classes.md) section. 47*57b5a4a6SAndroid Build Coastguard Worker 48*57b5a4a6SAndroid Build Coastguard WorkerAs a motivating example, let us take the following `Color` class with an integer value storing its `rgb` bytes. 49*57b5a4a6SAndroid Build Coastguard Worker 50*57b5a4a6SAndroid Build Coastguard Worker<!--- INCLUDE 51*57b5a4a6SAndroid Build Coastguard Workerimport kotlinx.serialization.* 52*57b5a4a6SAndroid Build Coastguard Workerimport kotlinx.serialization.json.* 53*57b5a4a6SAndroid Build Coastguard Worker--> 54*57b5a4a6SAndroid Build Coastguard Worker 55*57b5a4a6SAndroid Build Coastguard Worker```kotlin 56*57b5a4a6SAndroid Build Coastguard Worker@Serializable 57*57b5a4a6SAndroid Build Coastguard Workerclass Color(val rgb: Int) 58*57b5a4a6SAndroid Build Coastguard Worker 59*57b5a4a6SAndroid Build Coastguard Workerfun main() { 60*57b5a4a6SAndroid Build Coastguard Worker val green = Color(0x00ff00) 61*57b5a4a6SAndroid Build Coastguard Worker println(Json.encodeToString(green)) 62*57b5a4a6SAndroid Build Coastguard Worker} 63*57b5a4a6SAndroid Build Coastguard Worker``` 64*57b5a4a6SAndroid Build Coastguard Worker 65*57b5a4a6SAndroid Build Coastguard Worker> You can get the full code [here](../guide/example/example-serializer-01.kt). 66*57b5a4a6SAndroid Build Coastguard Worker 67*57b5a4a6SAndroid Build Coastguard WorkerBy default this class serializes its `rgb` property into JSON. 68*57b5a4a6SAndroid Build Coastguard Worker 69*57b5a4a6SAndroid Build Coastguard Worker```text 70*57b5a4a6SAndroid Build Coastguard Worker{"rgb":65280} 71*57b5a4a6SAndroid Build Coastguard Worker``` 72*57b5a4a6SAndroid Build Coastguard Worker 73*57b5a4a6SAndroid Build Coastguard Worker<!--- TEST --> 74*57b5a4a6SAndroid Build Coastguard Worker 75*57b5a4a6SAndroid Build Coastguard Worker### Plugin-generated serializer 76*57b5a4a6SAndroid Build Coastguard Worker 77*57b5a4a6SAndroid Build Coastguard WorkerEvery class marked with the `@Serializable` annotation, like the `Color` class from the previous example, 78*57b5a4a6SAndroid Build Coastguard Workergets an instance of the [KSerializer] interface automatically generated by the Kotlin Serialization compiler plugin. 79*57b5a4a6SAndroid Build Coastguard WorkerWe can retrieve this instance using the `.serializer()` function on the class's companion object. 80*57b5a4a6SAndroid Build Coastguard Worker 81*57b5a4a6SAndroid Build Coastguard WorkerWe can examine its [descriptor][KSerializer.descriptor] property that describes the structure of 82*57b5a4a6SAndroid Build Coastguard Workerthe serialized class. We'll learn more details about that in the upcoming sections. 83*57b5a4a6SAndroid Build Coastguard Worker 84*57b5a4a6SAndroid Build Coastguard Worker<!--- INCLUDE 85*57b5a4a6SAndroid Build Coastguard Workerimport kotlinx.serialization.* 86*57b5a4a6SAndroid Build Coastguard Worker 87*57b5a4a6SAndroid Build Coastguard Worker@Serializable 88*57b5a4a6SAndroid Build Coastguard Worker@SerialName("Color") 89*57b5a4a6SAndroid Build Coastguard Workerclass Color(val rgb: Int) 90*57b5a4a6SAndroid Build Coastguard Worker--> 91*57b5a4a6SAndroid Build Coastguard Worker 92*57b5a4a6SAndroid Build Coastguard Worker```kotlin 93*57b5a4a6SAndroid Build Coastguard Workerfun main() { 94*57b5a4a6SAndroid Build Coastguard Worker val colorSerializer: KSerializer<Color> = Color.serializer() 95*57b5a4a6SAndroid Build Coastguard Worker println(colorSerializer.descriptor) 96*57b5a4a6SAndroid Build Coastguard Worker} 97*57b5a4a6SAndroid Build Coastguard Worker``` 98*57b5a4a6SAndroid Build Coastguard Worker 99*57b5a4a6SAndroid Build Coastguard Worker> You can get the full code [here](../guide/example/example-serializer-02.kt). 100*57b5a4a6SAndroid Build Coastguard Worker 101*57b5a4a6SAndroid Build Coastguard Worker```text 102*57b5a4a6SAndroid Build Coastguard WorkerColor(rgb: kotlin.Int) 103*57b5a4a6SAndroid Build Coastguard Worker``` 104*57b5a4a6SAndroid Build Coastguard Worker 105*57b5a4a6SAndroid Build Coastguard Worker<!--- TEST --> 106*57b5a4a6SAndroid Build Coastguard Worker 107*57b5a4a6SAndroid Build Coastguard WorkerThis serializer is automatically retrieved and used by the Kotlin Serialization framework when the `Color` class 108*57b5a4a6SAndroid Build Coastguard Workeris itself serialized, or when it is used as a property of other classes. 109*57b5a4a6SAndroid Build Coastguard Worker 110*57b5a4a6SAndroid Build Coastguard Worker> You cannot define your own function `serializer()` on a companion object of a serializable class. 111*57b5a4a6SAndroid Build Coastguard Worker 112*57b5a4a6SAndroid Build Coastguard Worker### Plugin-generated generic serializer 113*57b5a4a6SAndroid Build Coastguard Worker 114*57b5a4a6SAndroid Build Coastguard WorkerFor generic classes, like the `Box` class shown in the [Generic classes](basic-serialization.md#generic-classes) section, 115*57b5a4a6SAndroid Build Coastguard Workerthe automatically generated `.serializer()` function accepts as many parameters as there are type parameters in the 116*57b5a4a6SAndroid Build Coastguard Workercorresponding class. These parameters are of type [KSerializer], so the actual type argument's serializer has 117*57b5a4a6SAndroid Build Coastguard Workerto be provided when constructing an instance of a serializer for a generic class. 118*57b5a4a6SAndroid Build Coastguard Worker 119*57b5a4a6SAndroid Build Coastguard Worker<!--- INCLUDE 120*57b5a4a6SAndroid Build Coastguard Workerimport kotlinx.serialization.* 121*57b5a4a6SAndroid Build Coastguard Worker 122*57b5a4a6SAndroid Build Coastguard Worker@Serializable 123*57b5a4a6SAndroid Build Coastguard Worker@SerialName("Color") 124*57b5a4a6SAndroid Build Coastguard Workerclass Color(val rgb: Int) 125*57b5a4a6SAndroid Build Coastguard Worker--> 126*57b5a4a6SAndroid Build Coastguard Worker 127*57b5a4a6SAndroid Build Coastguard Worker```kotlin 128*57b5a4a6SAndroid Build Coastguard Worker@Serializable 129*57b5a4a6SAndroid Build Coastguard Worker@SerialName("Box") 130*57b5a4a6SAndroid Build Coastguard Workerclass Box<T>(val contents: T) 131*57b5a4a6SAndroid Build Coastguard Worker 132*57b5a4a6SAndroid Build Coastguard Workerfun main() { 133*57b5a4a6SAndroid Build Coastguard Worker val boxedColorSerializer = Box.serializer(Color.serializer()) 134*57b5a4a6SAndroid Build Coastguard Worker println(boxedColorSerializer.descriptor) 135*57b5a4a6SAndroid Build Coastguard Worker} 136*57b5a4a6SAndroid Build Coastguard Worker``` 137*57b5a4a6SAndroid Build Coastguard Worker 138*57b5a4a6SAndroid Build Coastguard Worker> You can get the full code [here](../guide/example/example-serializer-03.kt). 139*57b5a4a6SAndroid Build Coastguard Worker 140*57b5a4a6SAndroid Build Coastguard WorkerAs we can see, a serializer was instantiated to serialize a concrete `Box<Color>`. 141*57b5a4a6SAndroid Build Coastguard Worker 142*57b5a4a6SAndroid Build Coastguard Worker```text 143*57b5a4a6SAndroid Build Coastguard WorkerBox(contents: Color) 144*57b5a4a6SAndroid Build Coastguard Worker``` 145*57b5a4a6SAndroid Build Coastguard Worker 146*57b5a4a6SAndroid Build Coastguard Worker<!--- TEST --> 147*57b5a4a6SAndroid Build Coastguard Worker 148*57b5a4a6SAndroid Build Coastguard Worker### Builtin primitive serializers 149*57b5a4a6SAndroid Build Coastguard Worker 150*57b5a4a6SAndroid Build Coastguard WorkerThe serializers for the [primitive builtin classes](builtin-classes.md#primitives) can be retrieved 151*57b5a4a6SAndroid Build Coastguard Workerusing `.serializer()` extensions. 152*57b5a4a6SAndroid Build Coastguard Worker 153*57b5a4a6SAndroid Build Coastguard Worker<!--- INCLUDE 154*57b5a4a6SAndroid Build Coastguard Workerimport kotlinx.serialization.* 155*57b5a4a6SAndroid Build Coastguard Workerimport kotlinx.serialization.builtins.* 156*57b5a4a6SAndroid Build Coastguard Worker--> 157*57b5a4a6SAndroid Build Coastguard Worker 158*57b5a4a6SAndroid Build Coastguard Worker```kotlin 159*57b5a4a6SAndroid Build Coastguard Workerfun main() { 160*57b5a4a6SAndroid Build Coastguard Worker val intSerializer: KSerializer<Int> = Int.serializer() 161*57b5a4a6SAndroid Build Coastguard Worker println(intSerializer.descriptor) 162*57b5a4a6SAndroid Build Coastguard Worker} 163*57b5a4a6SAndroid Build Coastguard Worker``` 164*57b5a4a6SAndroid Build Coastguard Worker 165*57b5a4a6SAndroid Build Coastguard Worker> You can get the full code [here](../guide/example/example-serializer-04.kt). 166*57b5a4a6SAndroid Build Coastguard Worker 167*57b5a4a6SAndroid Build Coastguard Worker<!--- TEST 168*57b5a4a6SAndroid Build Coastguard WorkerPrimitiveDescriptor(kotlin.Int) 169*57b5a4a6SAndroid Build Coastguard Worker--> 170*57b5a4a6SAndroid Build Coastguard Worker 171*57b5a4a6SAndroid Build Coastguard Worker### Constructing collection serializers 172*57b5a4a6SAndroid Build Coastguard Worker 173*57b5a4a6SAndroid Build Coastguard Worker[Builtin collection serializers](builtin-classes.md#lists), when needed, must be explicitly constructed 174*57b5a4a6SAndroid Build Coastguard Workerusing the corresponding functions [ListSerializer()], [SetSerializer()], [MapSerializer()], etc. 175*57b5a4a6SAndroid Build Coastguard WorkerThese classes are generic, so to instantiate their serializer we must provide the serializers for the 176*57b5a4a6SAndroid Build Coastguard Workercorresponding number of their type parameters. 177*57b5a4a6SAndroid Build Coastguard WorkerFor example, we can produce a serializer for a `List<String>` in the following way. 178*57b5a4a6SAndroid Build Coastguard Worker 179*57b5a4a6SAndroid Build Coastguard Worker<!--- INCLUDE 180*57b5a4a6SAndroid Build Coastguard Workerimport kotlinx.serialization.* 181*57b5a4a6SAndroid Build Coastguard Workerimport kotlinx.serialization.builtins.* 182*57b5a4a6SAndroid Build Coastguard Worker--> 183*57b5a4a6SAndroid Build Coastguard Worker 184*57b5a4a6SAndroid Build Coastguard Worker```kotlin 185*57b5a4a6SAndroid Build Coastguard Workerfun main() { 186*57b5a4a6SAndroid Build Coastguard Worker val stringListSerializer: KSerializer<List<String>> = ListSerializer(String.serializer()) 187*57b5a4a6SAndroid Build Coastguard Worker println(stringListSerializer.descriptor) 188*57b5a4a6SAndroid Build Coastguard Worker} 189*57b5a4a6SAndroid Build Coastguard Worker``` 190*57b5a4a6SAndroid Build Coastguard Worker 191*57b5a4a6SAndroid Build Coastguard Worker> You can get the full code [here](../guide/example/example-serializer-05.kt). 192*57b5a4a6SAndroid Build Coastguard Worker 193*57b5a4a6SAndroid Build Coastguard Worker<!--- TEST 194*57b5a4a6SAndroid Build Coastguard Workerkotlin.collections.ArrayList(PrimitiveDescriptor(kotlin.String)) 195*57b5a4a6SAndroid Build Coastguard Worker--> 196*57b5a4a6SAndroid Build Coastguard Worker 197*57b5a4a6SAndroid Build Coastguard Worker### Using top-level serializer function 198*57b5a4a6SAndroid Build Coastguard Worker 199*57b5a4a6SAndroid Build Coastguard WorkerWhen in doubt, you can always use the top-level generic `serializer<T>()` 200*57b5a4a6SAndroid Build Coastguard Workerfunction to retrieve a serializer for an arbitrary Kotlin type in your source-code. 201*57b5a4a6SAndroid Build Coastguard Worker 202*57b5a4a6SAndroid Build Coastguard Worker<!--- INCLUDE 203*57b5a4a6SAndroid Build Coastguard Workerimport kotlinx.serialization.* 204*57b5a4a6SAndroid Build Coastguard Worker--> 205*57b5a4a6SAndroid Build Coastguard Worker 206*57b5a4a6SAndroid Build Coastguard Worker```kotlin 207*57b5a4a6SAndroid Build Coastguard Worker@Serializable 208*57b5a4a6SAndroid Build Coastguard Worker@SerialName("Color") 209*57b5a4a6SAndroid Build Coastguard Workerclass Color(val rgb: Int) 210*57b5a4a6SAndroid Build Coastguard Worker 211*57b5a4a6SAndroid Build Coastguard Workerfun main() { 212*57b5a4a6SAndroid Build Coastguard Worker val stringToColorMapSerializer: KSerializer<Map<String, Color>> = serializer() 213*57b5a4a6SAndroid Build Coastguard Worker println(stringToColorMapSerializer.descriptor) 214*57b5a4a6SAndroid Build Coastguard Worker} 215*57b5a4a6SAndroid Build Coastguard Worker``` 216*57b5a4a6SAndroid Build Coastguard Worker 217*57b5a4a6SAndroid Build Coastguard Worker> You can get the full code [here](../guide/example/example-serializer-06.kt). 218*57b5a4a6SAndroid Build Coastguard Worker 219*57b5a4a6SAndroid Build Coastguard Worker<!--- TEST 220*57b5a4a6SAndroid Build Coastguard Workerkotlin.collections.LinkedHashMap(PrimitiveDescriptor(kotlin.String), Color(rgb: kotlin.Int)) 221*57b5a4a6SAndroid Build Coastguard Worker--> 222*57b5a4a6SAndroid Build Coastguard Worker 223*57b5a4a6SAndroid Build Coastguard Worker## Custom serializers 224*57b5a4a6SAndroid Build Coastguard Worker 225*57b5a4a6SAndroid Build Coastguard WorkerA plugin-generated serializer is convenient, but it may not produce the JSON we want 226*57b5a4a6SAndroid Build Coastguard Workerfor such a class as `Color`. Let's study alternatives. 227*57b5a4a6SAndroid Build Coastguard Worker 228*57b5a4a6SAndroid Build Coastguard Worker### Primitive serializer 229*57b5a4a6SAndroid Build Coastguard Worker 230*57b5a4a6SAndroid Build Coastguard WorkerWe want to serialize the `Color` class as a hex string with the green color represented as `"00ff00"`. 231*57b5a4a6SAndroid Build Coastguard WorkerTo achieve this, we write an object that implements the [KSerializer] interface for the `Color` class. 232*57b5a4a6SAndroid Build Coastguard Worker 233*57b5a4a6SAndroid Build Coastguard Worker<!--- INCLUDE .*-serializer-.* 234*57b5a4a6SAndroid Build Coastguard Workerimport kotlinx.serialization.* 235*57b5a4a6SAndroid Build Coastguard Workerimport kotlinx.serialization.json.* 236*57b5a4a6SAndroid Build Coastguard Workerimport kotlinx.serialization.encoding.* 237*57b5a4a6SAndroid Build Coastguard Workerimport kotlinx.serialization.descriptors.* 238*57b5a4a6SAndroid Build Coastguard Worker--> 239*57b5a4a6SAndroid Build Coastguard Worker 240*57b5a4a6SAndroid Build Coastguard Worker```kotlin 241*57b5a4a6SAndroid Build Coastguard Workerobject ColorAsStringSerializer : KSerializer<Color> { 242*57b5a4a6SAndroid Build Coastguard Worker override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Color", PrimitiveKind.STRING) 243*57b5a4a6SAndroid Build Coastguard Worker 244*57b5a4a6SAndroid Build Coastguard Worker override fun serialize(encoder: Encoder, value: Color) { 245*57b5a4a6SAndroid Build Coastguard Worker val string = value.rgb.toString(16).padStart(6, '0') 246*57b5a4a6SAndroid Build Coastguard Worker encoder.encodeString(string) 247*57b5a4a6SAndroid Build Coastguard Worker } 248*57b5a4a6SAndroid Build Coastguard Worker 249*57b5a4a6SAndroid Build Coastguard Worker override fun deserialize(decoder: Decoder): Color { 250*57b5a4a6SAndroid Build Coastguard Worker val string = decoder.decodeString() 251*57b5a4a6SAndroid Build Coastguard Worker return Color(string.toInt(16)) 252*57b5a4a6SAndroid Build Coastguard Worker } 253*57b5a4a6SAndroid Build Coastguard Worker} 254*57b5a4a6SAndroid Build Coastguard Worker``` 255*57b5a4a6SAndroid Build Coastguard Worker 256*57b5a4a6SAndroid Build Coastguard WorkerSerializer has three required pieces. 257*57b5a4a6SAndroid Build Coastguard Worker 258*57b5a4a6SAndroid Build Coastguard Worker* The [serialize][SerializationStrategy.serialize] function implements [SerializationStrategy]. 259*57b5a4a6SAndroid Build Coastguard Worker It receives an instance of [Encoder] and a value to serialize. 260*57b5a4a6SAndroid Build Coastguard Worker It uses the `encodeXxx` functions of `Encoder` to represent a value as a sequence of primitives. There is an 261*57b5a4a6SAndroid Build Coastguard Worker `encodeXxx` for each primitive type supported by serialization. 262*57b5a4a6SAndroid Build Coastguard Worker In our example, [encodeString][Encoder.encodeString] is used. 263*57b5a4a6SAndroid Build Coastguard Worker 264*57b5a4a6SAndroid Build Coastguard Worker* The [deserialize][DeserializationStrategy.deserialize] function implements [DeserializationStrategy]. 265*57b5a4a6SAndroid Build Coastguard Worker It receives an instance of [Decoder] and returns a 266*57b5a4a6SAndroid Build Coastguard Worker deserialized value. It uses the `decodeXxx` functions of `Decoder`, which mirror the corresponding functions of `Encoder`. 267*57b5a4a6SAndroid Build Coastguard Worker In our example [decodeString][Decoder.decodeString] is used. 268*57b5a4a6SAndroid Build Coastguard Worker 269*57b5a4a6SAndroid Build Coastguard Worker* The [descriptor][KSerializer.descriptor] property must faithfully explain what exactly the `encodeXxx` and `decodeXxx` 270*57b5a4a6SAndroid Build Coastguard Worker functions do so that a format implementation knows in advance what encoding/decoding methods they call. 271*57b5a4a6SAndroid Build Coastguard Worker Some formats might also use it to generate a schema for the serialized data. For primitive serialization, 272*57b5a4a6SAndroid Build Coastguard Worker the [PrimitiveSerialDescriptor][PrimitiveSerialDescriptor()] function must be used with a unique name of the 273*57b5a4a6SAndroid Build Coastguard Worker type that is being serialized. 274*57b5a4a6SAndroid Build Coastguard Worker [PrimitiveKind] describes the specific `encodeXxx`/`decodeXxx` method that is being used in the implementation. 275*57b5a4a6SAndroid Build Coastguard Worker 276*57b5a4a6SAndroid Build Coastguard Worker> When the `descriptor` does not correspond to the encoding/decoding methods, then the behavior of the resulting code 277*57b5a4a6SAndroid Build Coastguard Worker> is unspecified, and may arbitrarily change in future updates. 278*57b5a4a6SAndroid Build Coastguard Worker 279*57b5a4a6SAndroid Build Coastguard WorkerThe next step is to bind a serializer to a class. This is done with the [`@Serializable`][Serializable] annotation by adding 280*57b5a4a6SAndroid Build Coastguard Workerthe [`with`][Serializable.with] property value. 281*57b5a4a6SAndroid Build Coastguard Worker 282*57b5a4a6SAndroid Build Coastguard Worker```kotlin 283*57b5a4a6SAndroid Build Coastguard Worker@Serializable(with = ColorAsStringSerializer::class) 284*57b5a4a6SAndroid Build Coastguard Workerclass Color(val rgb: Int) 285*57b5a4a6SAndroid Build Coastguard Worker``` 286*57b5a4a6SAndroid Build Coastguard Worker 287*57b5a4a6SAndroid Build Coastguard WorkerNow we can serialize the `Color` class as we did before. 288*57b5a4a6SAndroid Build Coastguard Worker 289*57b5a4a6SAndroid Build Coastguard Worker```kotlin 290*57b5a4a6SAndroid Build Coastguard Workerfun main() { 291*57b5a4a6SAndroid Build Coastguard Worker val green = Color(0x00ff00) 292*57b5a4a6SAndroid Build Coastguard Worker println(Json.encodeToString(green)) 293*57b5a4a6SAndroid Build Coastguard Worker} 294*57b5a4a6SAndroid Build Coastguard Worker``` 295*57b5a4a6SAndroid Build Coastguard Worker 296*57b5a4a6SAndroid Build Coastguard Worker> You can get the full code [here](../guide/example/example-serializer-07.kt). 297*57b5a4a6SAndroid Build Coastguard Worker 298*57b5a4a6SAndroid Build Coastguard WorkerWe get the serial representation as the hex string we wanted. 299*57b5a4a6SAndroid Build Coastguard Worker 300*57b5a4a6SAndroid Build Coastguard Worker```text 301*57b5a4a6SAndroid Build Coastguard Worker"00ff00" 302*57b5a4a6SAndroid Build Coastguard Worker``` 303*57b5a4a6SAndroid Build Coastguard Worker 304*57b5a4a6SAndroid Build Coastguard Worker<!--- TEST --> 305*57b5a4a6SAndroid Build Coastguard Worker 306*57b5a4a6SAndroid Build Coastguard WorkerDeserialization is also straightforward because we implemented the `deserialize` method. 307*57b5a4a6SAndroid Build Coastguard Worker 308*57b5a4a6SAndroid Build Coastguard Worker<!--- INCLUDE 309*57b5a4a6SAndroid Build Coastguard Workerobject ColorAsStringSerializer : KSerializer<Color> { 310*57b5a4a6SAndroid Build Coastguard Worker override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Color", PrimitiveKind.STRING) 311*57b5a4a6SAndroid Build Coastguard Worker 312*57b5a4a6SAndroid Build Coastguard Worker override fun serialize(encoder: Encoder, value: Color) { 313*57b5a4a6SAndroid Build Coastguard Worker val string = value.rgb.toString(16).padStart(6, '0') 314*57b5a4a6SAndroid Build Coastguard Worker encoder.encodeString(string) 315*57b5a4a6SAndroid Build Coastguard Worker } 316*57b5a4a6SAndroid Build Coastguard Worker 317*57b5a4a6SAndroid Build Coastguard Worker override fun deserialize(decoder: Decoder): Color { 318*57b5a4a6SAndroid Build Coastguard Worker val string = decoder.decodeString() 319*57b5a4a6SAndroid Build Coastguard Worker return Color(string.toInt(16)) 320*57b5a4a6SAndroid Build Coastguard Worker } 321*57b5a4a6SAndroid Build Coastguard Worker} 322*57b5a4a6SAndroid Build Coastguard Worker--> 323*57b5a4a6SAndroid Build Coastguard Worker 324*57b5a4a6SAndroid Build Coastguard Worker```kotlin 325*57b5a4a6SAndroid Build Coastguard Worker@Serializable(with = ColorAsStringSerializer::class) 326*57b5a4a6SAndroid Build Coastguard Workerclass Color(val rgb: Int) 327*57b5a4a6SAndroid Build Coastguard Worker 328*57b5a4a6SAndroid Build Coastguard Workerfun main() { 329*57b5a4a6SAndroid Build Coastguard Worker val color = Json.decodeFromString<Color>("\"00ff00\"") 330*57b5a4a6SAndroid Build Coastguard Worker println(color.rgb) // prints 65280 331*57b5a4a6SAndroid Build Coastguard Worker} 332*57b5a4a6SAndroid Build Coastguard Worker``` 333*57b5a4a6SAndroid Build Coastguard Worker 334*57b5a4a6SAndroid Build Coastguard Worker> You can get the full code [here](../guide/example/example-serializer-08.kt). 335*57b5a4a6SAndroid Build Coastguard Worker 336*57b5a4a6SAndroid Build Coastguard Worker<!--- TEST 337*57b5a4a6SAndroid Build Coastguard Worker65280 338*57b5a4a6SAndroid Build Coastguard Worker--> 339*57b5a4a6SAndroid Build Coastguard Worker 340*57b5a4a6SAndroid Build Coastguard WorkerIt also works if we serialize or deserialize a different class with `Color` properties. 341*57b5a4a6SAndroid Build Coastguard Worker 342*57b5a4a6SAndroid Build Coastguard Worker<!--- INCLUDE 343*57b5a4a6SAndroid Build Coastguard Workerobject ColorAsStringSerializer : KSerializer<Color> { 344*57b5a4a6SAndroid Build Coastguard Worker override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Color", PrimitiveKind.STRING) 345*57b5a4a6SAndroid Build Coastguard Worker 346*57b5a4a6SAndroid Build Coastguard Worker override fun serialize(encoder: Encoder, value: Color) { 347*57b5a4a6SAndroid Build Coastguard Worker val string = value.rgb.toString(16).padStart(6, '0') 348*57b5a4a6SAndroid Build Coastguard Worker encoder.encodeString(string) 349*57b5a4a6SAndroid Build Coastguard Worker } 350*57b5a4a6SAndroid Build Coastguard Worker 351*57b5a4a6SAndroid Build Coastguard Worker override fun deserialize(decoder: Decoder): Color { 352*57b5a4a6SAndroid Build Coastguard Worker val string = decoder.decodeString() 353*57b5a4a6SAndroid Build Coastguard Worker return Color(string.toInt(16)) 354*57b5a4a6SAndroid Build Coastguard Worker } 355*57b5a4a6SAndroid Build Coastguard Worker} 356*57b5a4a6SAndroid Build Coastguard Worker--> 357*57b5a4a6SAndroid Build Coastguard Worker 358*57b5a4a6SAndroid Build Coastguard Worker```kotlin 359*57b5a4a6SAndroid Build Coastguard Worker@Serializable(with = ColorAsStringSerializer::class) 360*57b5a4a6SAndroid Build Coastguard Workerdata class Color(val rgb: Int) 361*57b5a4a6SAndroid Build Coastguard Worker 362*57b5a4a6SAndroid Build Coastguard Worker@Serializable 363*57b5a4a6SAndroid Build Coastguard Workerdata class Settings(val background: Color, val foreground: Color) 364*57b5a4a6SAndroid Build Coastguard Worker 365*57b5a4a6SAndroid Build Coastguard Workerfun main() { 366*57b5a4a6SAndroid Build Coastguard Worker val data = Settings(Color(0xffffff), Color(0)) 367*57b5a4a6SAndroid Build Coastguard Worker val string = Json.encodeToString(data) 368*57b5a4a6SAndroid Build Coastguard Worker println(string) 369*57b5a4a6SAndroid Build Coastguard Worker require(Json.decodeFromString<Settings>(string) == data) 370*57b5a4a6SAndroid Build Coastguard Worker} 371*57b5a4a6SAndroid Build Coastguard Worker``` 372*57b5a4a6SAndroid Build Coastguard Worker 373*57b5a4a6SAndroid Build Coastguard Worker> You can get the full code [here](../guide/example/example-serializer-09.kt). 374*57b5a4a6SAndroid Build Coastguard Worker 375*57b5a4a6SAndroid Build Coastguard WorkerBoth `Color` properties are serialized as strings. 376*57b5a4a6SAndroid Build Coastguard Worker 377*57b5a4a6SAndroid Build Coastguard Worker```text 378*57b5a4a6SAndroid Build Coastguard Worker{"background":"ffffff","foreground":"000000"} 379*57b5a4a6SAndroid Build Coastguard Worker``` 380*57b5a4a6SAndroid Build Coastguard Worker 381*57b5a4a6SAndroid Build Coastguard Worker<!--- TEST --> 382*57b5a4a6SAndroid Build Coastguard Worker 383*57b5a4a6SAndroid Build Coastguard Worker### Delegating serializers 384*57b5a4a6SAndroid Build Coastguard Worker 385*57b5a4a6SAndroid Build Coastguard WorkerIn the previous example, we represented the `Color` class as a string. 386*57b5a4a6SAndroid Build Coastguard WorkerString is considered to be a primitive type, therefore we used `PrimitiveClassDescriptor` and specialized `encodeString` method. 387*57b5a4a6SAndroid Build Coastguard WorkerNow let's see what our actions would be if we have to serialize `Color` as another non-primitive type, let's say `IntArray`. 388*57b5a4a6SAndroid Build Coastguard Worker 389*57b5a4a6SAndroid Build Coastguard WorkerAn implementation of [KSerializer] for our original `Color` class is going to perform a conversion between 390*57b5a4a6SAndroid Build Coastguard Worker`Color` and `IntArray`, but delegate the actual serialization logic to the `IntArraySerializer` 391*57b5a4a6SAndroid Build Coastguard Workerusing [encodeSerializableValue][Encoder.encodeSerializableValue] and 392*57b5a4a6SAndroid Build Coastguard Worker[decodeSerializableValue][Decoder.decodeSerializableValue]. 393*57b5a4a6SAndroid Build Coastguard Worker 394*57b5a4a6SAndroid Build Coastguard Worker```kotlin 395*57b5a4a6SAndroid Build Coastguard Workerimport kotlinx.serialization.builtins.IntArraySerializer 396*57b5a4a6SAndroid Build Coastguard Worker 397*57b5a4a6SAndroid Build Coastguard Workerclass ColorIntArraySerializer : KSerializer<Color> { 398*57b5a4a6SAndroid Build Coastguard Worker private val delegateSerializer = IntArraySerializer() 399*57b5a4a6SAndroid Build Coastguard Worker override val descriptor = SerialDescriptor("Color", delegateSerializer.descriptor) 400*57b5a4a6SAndroid Build Coastguard Worker 401*57b5a4a6SAndroid Build Coastguard Worker override fun serialize(encoder: Encoder, value: Color) { 402*57b5a4a6SAndroid Build Coastguard Worker val data = intArrayOf( 403*57b5a4a6SAndroid Build Coastguard Worker (value.rgb shr 16) and 0xFF, 404*57b5a4a6SAndroid Build Coastguard Worker (value.rgb shr 8) and 0xFF, 405*57b5a4a6SAndroid Build Coastguard Worker value.rgb and 0xFF 406*57b5a4a6SAndroid Build Coastguard Worker ) 407*57b5a4a6SAndroid Build Coastguard Worker encoder.encodeSerializableValue(delegateSerializer, data) 408*57b5a4a6SAndroid Build Coastguard Worker } 409*57b5a4a6SAndroid Build Coastguard Worker 410*57b5a4a6SAndroid Build Coastguard Worker override fun deserialize(decoder: Decoder): Color { 411*57b5a4a6SAndroid Build Coastguard Worker val array = decoder.decodeSerializableValue(delegateSerializer) 412*57b5a4a6SAndroid Build Coastguard Worker return Color((array[0] shl 16) or (array[1] shl 8) or array[2]) 413*57b5a4a6SAndroid Build Coastguard Worker } 414*57b5a4a6SAndroid Build Coastguard Worker} 415*57b5a4a6SAndroid Build Coastguard Worker``` 416*57b5a4a6SAndroid Build Coastguard Worker 417*57b5a4a6SAndroid Build Coastguard WorkerNote that we can't use default `Color.serializer().descriptor` here because formats that rely 418*57b5a4a6SAndroid Build Coastguard Workeron the schema may think that we would call `encodeInt` instead of `encodeSerializableValue`. 419*57b5a4a6SAndroid Build Coastguard WorkerNeither we can use `IntArraySerializer().descriptor` directly — otherwise, formats that handle int arrays specially 420*57b5a4a6SAndroid Build Coastguard Workercan't tell if `value` is really a `IntArray` or a `Color`. Don't worry, this optimization would still kick in 421*57b5a4a6SAndroid Build Coastguard Workerwhen serializing actual underlying int array. 422*57b5a4a6SAndroid Build Coastguard Worker 423*57b5a4a6SAndroid Build Coastguard Worker> Example of how format can treat arrays specially is shown in the [formats guide](formats.md#format-specific-types). 424*57b5a4a6SAndroid Build Coastguard Worker 425*57b5a4a6SAndroid Build Coastguard WorkerNow we can use the serializer: 426*57b5a4a6SAndroid Build Coastguard Worker 427*57b5a4a6SAndroid Build Coastguard Worker```kotlin 428*57b5a4a6SAndroid Build Coastguard Worker@Serializable(with = ColorIntArraySerializer::class) 429*57b5a4a6SAndroid Build Coastguard Workerclass Color(val rgb: Int) 430*57b5a4a6SAndroid Build Coastguard Worker 431*57b5a4a6SAndroid Build Coastguard Workerfun main() { 432*57b5a4a6SAndroid Build Coastguard Worker val green = Color(0x00ff00) 433*57b5a4a6SAndroid Build Coastguard Worker println(Json.encodeToString(green)) 434*57b5a4a6SAndroid Build Coastguard Worker} 435*57b5a4a6SAndroid Build Coastguard Worker``` 436*57b5a4a6SAndroid Build Coastguard Worker 437*57b5a4a6SAndroid Build Coastguard WorkerAs you can see, such array representation is not very useful in JSON, 438*57b5a4a6SAndroid Build Coastguard Workerbut may save some space when used with a `ByteArray` and a binary format. 439*57b5a4a6SAndroid Build Coastguard Worker 440*57b5a4a6SAndroid Build Coastguard Worker> You can get the full code [here](../guide/example/example-serializer-10.kt). 441*57b5a4a6SAndroid Build Coastguard Worker 442*57b5a4a6SAndroid Build Coastguard Worker```text 443*57b5a4a6SAndroid Build Coastguard Worker[0,255,0] 444*57b5a4a6SAndroid Build Coastguard Worker``` 445*57b5a4a6SAndroid Build Coastguard Worker 446*57b5a4a6SAndroid Build Coastguard Worker<!--- TEST --> 447*57b5a4a6SAndroid Build Coastguard Worker 448*57b5a4a6SAndroid Build Coastguard Worker 449*57b5a4a6SAndroid Build Coastguard Worker### Composite serializer via surrogate 450*57b5a4a6SAndroid Build Coastguard Worker 451*57b5a4a6SAndroid Build Coastguard WorkerNow our challenge is to get `Color` serialized so that it is represented in JSON as if it is a class 452*57b5a4a6SAndroid Build Coastguard Workerwith three properties—`r`, `g`, and `b`—so that JSON encodes it as an object. 453*57b5a4a6SAndroid Build Coastguard WorkerThe easiest way to achieve this is to define a _surrogate_ class mimicking the serialized form of `Color` that 454*57b5a4a6SAndroid Build Coastguard Workerwe are going to use for its serialization. We also set the [SerialName] of this surrogate class to `Color`. Then if 455*57b5a4a6SAndroid Build Coastguard Workerany format uses this name the surrogate looks like it is a `Color` class. 456*57b5a4a6SAndroid Build Coastguard WorkerThe surrogate class can be `private`, and can enforce all the constraints on the serial representation 457*57b5a4a6SAndroid Build Coastguard Workerof the class in its `init` block. 458*57b5a4a6SAndroid Build Coastguard Worker 459*57b5a4a6SAndroid Build Coastguard Worker```kotlin 460*57b5a4a6SAndroid Build Coastguard Worker@Serializable 461*57b5a4a6SAndroid Build Coastguard Worker@SerialName("Color") 462*57b5a4a6SAndroid Build Coastguard Workerprivate class ColorSurrogate(val r: Int, val g: Int, val b: Int) { 463*57b5a4a6SAndroid Build Coastguard Worker init { 464*57b5a4a6SAndroid Build Coastguard Worker require(r in 0..255 && g in 0..255 && b in 0..255) 465*57b5a4a6SAndroid Build Coastguard Worker } 466*57b5a4a6SAndroid Build Coastguard Worker} 467*57b5a4a6SAndroid Build Coastguard Worker``` 468*57b5a4a6SAndroid Build Coastguard Worker 469*57b5a4a6SAndroid Build Coastguard Worker> An example of where the class name is used is shown in 470*57b5a4a6SAndroid Build Coastguard Worker> the [Custom subclass serial name](polymorphism.md#custom-subclass-serial-name) section in the chapter on polymorphism. 471*57b5a4a6SAndroid Build Coastguard Worker 472*57b5a4a6SAndroid Build Coastguard WorkerNow we can use the `ColorSurrogate.serializer()` function to retrieve a plugin-generated serializer for the 473*57b5a4a6SAndroid Build Coastguard Workersurrogate class. 474*57b5a4a6SAndroid Build Coastguard Worker 475*57b5a4a6SAndroid Build Coastguard WorkerWe can use the same approach as in [delegating serializer](#delegating-serializers), but this time, 476*57b5a4a6SAndroid Build Coastguard Workerwe are fully reusing an automatically 477*57b5a4a6SAndroid Build Coastguard Workergenerated [SerialDescriptor] for the surrogate because it should be indistinguishable from the original. 478*57b5a4a6SAndroid Build Coastguard Worker 479*57b5a4a6SAndroid Build Coastguard Worker```kotlin 480*57b5a4a6SAndroid Build Coastguard Workerobject ColorSerializer : KSerializer<Color> { 481*57b5a4a6SAndroid Build Coastguard Worker override val descriptor: SerialDescriptor = ColorSurrogate.serializer().descriptor 482*57b5a4a6SAndroid Build Coastguard Worker 483*57b5a4a6SAndroid Build Coastguard Worker override fun serialize(encoder: Encoder, value: Color) { 484*57b5a4a6SAndroid Build Coastguard Worker val surrogate = ColorSurrogate((value.rgb shr 16) and 0xff, (value.rgb shr 8) and 0xff, value.rgb and 0xff) 485*57b5a4a6SAndroid Build Coastguard Worker encoder.encodeSerializableValue(ColorSurrogate.serializer(), surrogate) 486*57b5a4a6SAndroid Build Coastguard Worker } 487*57b5a4a6SAndroid Build Coastguard Worker 488*57b5a4a6SAndroid Build Coastguard Worker override fun deserialize(decoder: Decoder): Color { 489*57b5a4a6SAndroid Build Coastguard Worker val surrogate = decoder.decodeSerializableValue(ColorSurrogate.serializer()) 490*57b5a4a6SAndroid Build Coastguard Worker return Color((surrogate.r shl 16) or (surrogate.g shl 8) or surrogate.b) 491*57b5a4a6SAndroid Build Coastguard Worker } 492*57b5a4a6SAndroid Build Coastguard Worker} 493*57b5a4a6SAndroid Build Coastguard Worker``` 494*57b5a4a6SAndroid Build Coastguard Worker 495*57b5a4a6SAndroid Build Coastguard WorkerWe bind the `ColorSerializer` serializer to the `Color` class. 496*57b5a4a6SAndroid Build Coastguard Worker 497*57b5a4a6SAndroid Build Coastguard Worker```kotlin 498*57b5a4a6SAndroid Build Coastguard Worker@Serializable(with = ColorSerializer::class) 499*57b5a4a6SAndroid Build Coastguard Workerclass Color(val rgb: Int) 500*57b5a4a6SAndroid Build Coastguard Worker``` 501*57b5a4a6SAndroid Build Coastguard Worker 502*57b5a4a6SAndroid Build Coastguard WorkerNow we can enjoy the result of serialization for the `Color` class. 503*57b5a4a6SAndroid Build Coastguard Worker 504*57b5a4a6SAndroid Build Coastguard Worker<!--- INCLUDE 505*57b5a4a6SAndroid Build Coastguard Workerfun main() { 506*57b5a4a6SAndroid Build Coastguard Worker val green = Color(0x00ff00) 507*57b5a4a6SAndroid Build Coastguard Worker println(Json.encodeToString(green)) 508*57b5a4a6SAndroid Build Coastguard Worker} 509*57b5a4a6SAndroid Build Coastguard Worker--> 510*57b5a4a6SAndroid Build Coastguard Worker 511*57b5a4a6SAndroid Build Coastguard Worker> You can get the full code [here](../guide/example/example-serializer-11.kt). 512*57b5a4a6SAndroid Build Coastguard Worker 513*57b5a4a6SAndroid Build Coastguard Worker```text 514*57b5a4a6SAndroid Build Coastguard Worker{"r":0,"g":255,"b":0} 515*57b5a4a6SAndroid Build Coastguard Worker``` 516*57b5a4a6SAndroid Build Coastguard Worker 517*57b5a4a6SAndroid Build Coastguard Worker<!--- TEST --> 518*57b5a4a6SAndroid Build Coastguard Worker 519*57b5a4a6SAndroid Build Coastguard Worker### Hand-written composite serializer 520*57b5a4a6SAndroid Build Coastguard Worker 521*57b5a4a6SAndroid Build Coastguard WorkerThere are some cases where a surrogate solution does not fit. Perhaps we want to avoid the performance 522*57b5a4a6SAndroid Build Coastguard Workerimplications of additional allocation, or we want a configurable/dynamic set of properties for the 523*57b5a4a6SAndroid Build Coastguard Workerresulting serial representation. In these cases we need to manually write a class 524*57b5a4a6SAndroid Build Coastguard Workerserializer which mimics the behaviour of a generated serializer. 525*57b5a4a6SAndroid Build Coastguard Worker 526*57b5a4a6SAndroid Build Coastguard Worker```kotlin 527*57b5a4a6SAndroid Build Coastguard Workerobject ColorAsObjectSerializer : KSerializer<Color> { 528*57b5a4a6SAndroid Build Coastguard Worker``` 529*57b5a4a6SAndroid Build Coastguard Worker 530*57b5a4a6SAndroid Build Coastguard WorkerLet's introduce it piece by piece. First, a descriptor is defined using the [buildClassSerialDescriptor] builder. 531*57b5a4a6SAndroid Build Coastguard WorkerThe [element][ClassSerialDescriptorBuilder.element] function in the builder DSL automatically fetches serializers 532*57b5a4a6SAndroid Build Coastguard Workerfor the corresponding fields by their type. The order of elements is important. They are indexed starting from zero. 533*57b5a4a6SAndroid Build Coastguard Worker 534*57b5a4a6SAndroid Build Coastguard Worker```kotlin 535*57b5a4a6SAndroid Build Coastguard Worker override val descriptor: SerialDescriptor = 536*57b5a4a6SAndroid Build Coastguard Worker buildClassSerialDescriptor("Color") { 537*57b5a4a6SAndroid Build Coastguard Worker element<Int>("r") 538*57b5a4a6SAndroid Build Coastguard Worker element<Int>("g") 539*57b5a4a6SAndroid Build Coastguard Worker element<Int>("b") 540*57b5a4a6SAndroid Build Coastguard Worker } 541*57b5a4a6SAndroid Build Coastguard Worker``` 542*57b5a4a6SAndroid Build Coastguard Worker 543*57b5a4a6SAndroid Build Coastguard Worker> The "element" is a generic term here. What is an element of a descriptor depends on its [SerialKind]. 544*57b5a4a6SAndroid Build Coastguard Worker> Elements of a class descriptor are its properties, elements of a enum descriptor are its cases, etc. 545*57b5a4a6SAndroid Build Coastguard Worker 546*57b5a4a6SAndroid Build Coastguard WorkerThen we write the `serialize` function using the [encodeStructure] DSL that provides access to 547*57b5a4a6SAndroid Build Coastguard Workerthe [CompositeEncoder] in its block. The difference between [Encoder] and [CompositeEncoder] is the latter 548*57b5a4a6SAndroid Build Coastguard Workerhas `encodeXxxElement` functions that correspond to the `encodeXxx` functions of the former. They must be called 549*57b5a4a6SAndroid Build Coastguard Workerin the same order as in the descriptor. 550*57b5a4a6SAndroid Build Coastguard Worker 551*57b5a4a6SAndroid Build Coastguard Worker```kotlin 552*57b5a4a6SAndroid Build Coastguard Worker override fun serialize(encoder: Encoder, value: Color) = 553*57b5a4a6SAndroid Build Coastguard Worker encoder.encodeStructure(descriptor) { 554*57b5a4a6SAndroid Build Coastguard Worker encodeIntElement(descriptor, 0, (value.rgb shr 16) and 0xff) 555*57b5a4a6SAndroid Build Coastguard Worker encodeIntElement(descriptor, 1, (value.rgb shr 8) and 0xff) 556*57b5a4a6SAndroid Build Coastguard Worker encodeIntElement(descriptor, 2, value.rgb and 0xff) 557*57b5a4a6SAndroid Build Coastguard Worker } 558*57b5a4a6SAndroid Build Coastguard Worker``` 559*57b5a4a6SAndroid Build Coastguard Worker 560*57b5a4a6SAndroid Build Coastguard WorkerThe most complex piece of code is the `deserialize` function. It must support formats, like JSON, that 561*57b5a4a6SAndroid Build Coastguard Workercan decode properties in an arbitrary order. It starts with the call to [decodeStructure] to 562*57b5a4a6SAndroid Build Coastguard Workerget access to a [CompositeDecoder]. Inside it we write a loop that repeatedly calls 563*57b5a4a6SAndroid Build Coastguard Worker[decodeElementIndex][CompositeDecoder.decodeElementIndex] to decode the index of the next element, then we decode the corresponding 564*57b5a4a6SAndroid Build Coastguard Workerelement using [decodeIntElement][CompositeDecoder.decodeIntElement] in our example, and finally we terminate the loop when 565*57b5a4a6SAndroid Build Coastguard Worker`CompositeDecoder.DECODE_DONE` is encountered. 566*57b5a4a6SAndroid Build Coastguard Worker 567*57b5a4a6SAndroid Build Coastguard Worker```kotlin 568*57b5a4a6SAndroid Build Coastguard Worker override fun deserialize(decoder: Decoder): Color = 569*57b5a4a6SAndroid Build Coastguard Worker decoder.decodeStructure(descriptor) { 570*57b5a4a6SAndroid Build Coastguard Worker var r = -1 571*57b5a4a6SAndroid Build Coastguard Worker var g = -1 572*57b5a4a6SAndroid Build Coastguard Worker var b = -1 573*57b5a4a6SAndroid Build Coastguard Worker while (true) { 574*57b5a4a6SAndroid Build Coastguard Worker when (val index = decodeElementIndex(descriptor)) { 575*57b5a4a6SAndroid Build Coastguard Worker 0 -> r = decodeIntElement(descriptor, 0) 576*57b5a4a6SAndroid Build Coastguard Worker 1 -> g = decodeIntElement(descriptor, 1) 577*57b5a4a6SAndroid Build Coastguard Worker 2 -> b = decodeIntElement(descriptor, 2) 578*57b5a4a6SAndroid Build Coastguard Worker CompositeDecoder.DECODE_DONE -> break 579*57b5a4a6SAndroid Build Coastguard Worker else -> error("Unexpected index: $index") 580*57b5a4a6SAndroid Build Coastguard Worker } 581*57b5a4a6SAndroid Build Coastguard Worker } 582*57b5a4a6SAndroid Build Coastguard Worker require(r in 0..255 && g in 0..255 && b in 0..255) 583*57b5a4a6SAndroid Build Coastguard Worker Color((r shl 16) or (g shl 8) or b) 584*57b5a4a6SAndroid Build Coastguard Worker } 585*57b5a4a6SAndroid Build Coastguard Worker``` 586*57b5a4a6SAndroid Build Coastguard Worker 587*57b5a4a6SAndroid Build Coastguard Worker<!--- INCLUDE 588*57b5a4a6SAndroid Build Coastguard Worker} 589*57b5a4a6SAndroid Build Coastguard Worker--> 590*57b5a4a6SAndroid Build Coastguard Worker 591*57b5a4a6SAndroid Build Coastguard WorkerNow we bind the resulting serializer to the `Color` class and test its serialization/deserialization. 592*57b5a4a6SAndroid Build Coastguard Worker 593*57b5a4a6SAndroid Build Coastguard Worker```kotlin 594*57b5a4a6SAndroid Build Coastguard Worker@Serializable(with = ColorAsObjectSerializer::class) 595*57b5a4a6SAndroid Build Coastguard Workerdata class Color(val rgb: Int) 596*57b5a4a6SAndroid Build Coastguard Worker 597*57b5a4a6SAndroid Build Coastguard Workerfun main() { 598*57b5a4a6SAndroid Build Coastguard Worker val color = Color(0x00ff00) 599*57b5a4a6SAndroid Build Coastguard Worker val string = Json.encodeToString(color) 600*57b5a4a6SAndroid Build Coastguard Worker println(string) 601*57b5a4a6SAndroid Build Coastguard Worker require(Json.decodeFromString<Color>(string) == color) 602*57b5a4a6SAndroid Build Coastguard Worker} 603*57b5a4a6SAndroid Build Coastguard Worker``` 604*57b5a4a6SAndroid Build Coastguard Worker 605*57b5a4a6SAndroid Build Coastguard Worker> You can get the full code [here](../guide/example/example-serializer-12.kt). 606*57b5a4a6SAndroid Build Coastguard Worker 607*57b5a4a6SAndroid Build Coastguard WorkerAs before, we got the `Color` class represented as a JSON object with three keys: 608*57b5a4a6SAndroid Build Coastguard Worker 609*57b5a4a6SAndroid Build Coastguard Worker```text 610*57b5a4a6SAndroid Build Coastguard Worker{"r":0,"g":255,"b":0} 611*57b5a4a6SAndroid Build Coastguard Worker``` 612*57b5a4a6SAndroid Build Coastguard Worker 613*57b5a4a6SAndroid Build Coastguard Worker<!--- TEST --> 614*57b5a4a6SAndroid Build Coastguard Worker 615*57b5a4a6SAndroid Build Coastguard Worker### Sequential decoding protocol (experimental) 616*57b5a4a6SAndroid Build Coastguard Worker 617*57b5a4a6SAndroid Build Coastguard WorkerThe implementation of the `deserialize` function from the previous section works with any format. However, 618*57b5a4a6SAndroid Build Coastguard Workersome formats either always store all the complex data in order, or only do so sometimes (JSON always stores 619*57b5a4a6SAndroid Build Coastguard Workercollections in order). With these formats the complex protocol of calling `decodeElementIndex` in the loop is 620*57b5a4a6SAndroid Build Coastguard Workernot needed, and a faster implementation can be used if the [CompositeDecoder.decodeSequentially] function returns `true`. 621*57b5a4a6SAndroid Build Coastguard WorkerThe plugin-generated serializers are actually conceptually similar to the below code. 622*57b5a4a6SAndroid Build Coastguard Worker 623*57b5a4a6SAndroid Build Coastguard Worker<!--- INCLUDE 624*57b5a4a6SAndroid Build Coastguard Workerobject ColorAsObjectSerializer : KSerializer<Color> { 625*57b5a4a6SAndroid Build Coastguard Worker 626*57b5a4a6SAndroid Build Coastguard Worker override val descriptor: SerialDescriptor = 627*57b5a4a6SAndroid Build Coastguard Worker buildClassSerialDescriptor("Color") { 628*57b5a4a6SAndroid Build Coastguard Worker element<Int>("r") 629*57b5a4a6SAndroid Build Coastguard Worker element<Int>("g") 630*57b5a4a6SAndroid Build Coastguard Worker element<Int>("b") 631*57b5a4a6SAndroid Build Coastguard Worker } 632*57b5a4a6SAndroid Build Coastguard Worker 633*57b5a4a6SAndroid Build Coastguard Worker override fun serialize(encoder: Encoder, value: Color) = 634*57b5a4a6SAndroid Build Coastguard Worker encoder.encodeStructure(descriptor) { 635*57b5a4a6SAndroid Build Coastguard Worker encodeIntElement(descriptor, 0, (value.rgb shr 16) and 0xff) 636*57b5a4a6SAndroid Build Coastguard Worker encodeIntElement(descriptor, 1, (value.rgb shr 8) and 0xff) 637*57b5a4a6SAndroid Build Coastguard Worker encodeIntElement(descriptor, 2, value.rgb and 0xff) 638*57b5a4a6SAndroid Build Coastguard Worker } 639*57b5a4a6SAndroid Build Coastguard Worker--> 640*57b5a4a6SAndroid Build Coastguard Worker 641*57b5a4a6SAndroid Build Coastguard Worker```kotlin 642*57b5a4a6SAndroid Build Coastguard Worker override fun deserialize(decoder: Decoder): Color = 643*57b5a4a6SAndroid Build Coastguard Worker decoder.decodeStructure(descriptor) { 644*57b5a4a6SAndroid Build Coastguard Worker var r = -1 645*57b5a4a6SAndroid Build Coastguard Worker var g = -1 646*57b5a4a6SAndroid Build Coastguard Worker var b = -1 647*57b5a4a6SAndroid Build Coastguard Worker if (decodeSequentially()) { // sequential decoding protocol 648*57b5a4a6SAndroid Build Coastguard Worker r = decodeIntElement(descriptor, 0) 649*57b5a4a6SAndroid Build Coastguard Worker g = decodeIntElement(descriptor, 1) 650*57b5a4a6SAndroid Build Coastguard Worker b = decodeIntElement(descriptor, 2) 651*57b5a4a6SAndroid Build Coastguard Worker } else while (true) { 652*57b5a4a6SAndroid Build Coastguard Worker when (val index = decodeElementIndex(descriptor)) { 653*57b5a4a6SAndroid Build Coastguard Worker 0 -> r = decodeIntElement(descriptor, 0) 654*57b5a4a6SAndroid Build Coastguard Worker 1 -> g = decodeIntElement(descriptor, 1) 655*57b5a4a6SAndroid Build Coastguard Worker 2 -> b = decodeIntElement(descriptor, 2) 656*57b5a4a6SAndroid Build Coastguard Worker CompositeDecoder.DECODE_DONE -> break 657*57b5a4a6SAndroid Build Coastguard Worker else -> error("Unexpected index: $index") 658*57b5a4a6SAndroid Build Coastguard Worker } 659*57b5a4a6SAndroid Build Coastguard Worker } 660*57b5a4a6SAndroid Build Coastguard Worker require(r in 0..255 && g in 0..255 && b in 0..255) 661*57b5a4a6SAndroid Build Coastguard Worker Color((r shl 16) or (g shl 8) or b) 662*57b5a4a6SAndroid Build Coastguard Worker } 663*57b5a4a6SAndroid Build Coastguard Worker``` 664*57b5a4a6SAndroid Build Coastguard Worker 665*57b5a4a6SAndroid Build Coastguard Worker<!--- INCLUDE 666*57b5a4a6SAndroid Build Coastguard Worker} 667*57b5a4a6SAndroid Build Coastguard Worker 668*57b5a4a6SAndroid Build Coastguard Worker@Serializable(with = ColorAsObjectSerializer::class) 669*57b5a4a6SAndroid Build Coastguard Workerdata class Color(val rgb: Int) 670*57b5a4a6SAndroid Build Coastguard Worker 671*57b5a4a6SAndroid Build Coastguard Workerfun main() { 672*57b5a4a6SAndroid Build Coastguard Worker val color = Color(0x00ff00) 673*57b5a4a6SAndroid Build Coastguard Worker val string = Json.encodeToString(color) 674*57b5a4a6SAndroid Build Coastguard Worker println(string) 675*57b5a4a6SAndroid Build Coastguard Worker require(Json.decodeFromString<Color>(string) == color) 676*57b5a4a6SAndroid Build Coastguard Worker} 677*57b5a4a6SAndroid Build Coastguard Worker--> 678*57b5a4a6SAndroid Build Coastguard Worker 679*57b5a4a6SAndroid Build Coastguard Worker> You can get the full code [here](../guide/example/example-serializer-13.kt). 680*57b5a4a6SAndroid Build Coastguard Worker 681*57b5a4a6SAndroid Build Coastguard Worker<!--- TEST 682*57b5a4a6SAndroid Build Coastguard Worker{"r":0,"g":255,"b":0} 683*57b5a4a6SAndroid Build Coastguard Worker--> 684*57b5a4a6SAndroid Build Coastguard Worker 685*57b5a4a6SAndroid Build Coastguard Worker### Serializing 3rd party classes 686*57b5a4a6SAndroid Build Coastguard Worker 687*57b5a4a6SAndroid Build Coastguard WorkerSometimes an application has to work with an external type that is not serializable. 688*57b5a4a6SAndroid Build Coastguard WorkerLet us use [java.util.Date] as an example. As before, we start by writing an implementation of [KSerializer] 689*57b5a4a6SAndroid Build Coastguard Workerfor the class. Our goal is to get a `Date` serialized as a long number of milliseconds following the 690*57b5a4a6SAndroid Build Coastguard Workerapproach from the [Primitive serializer](#primitive-serializer) section. 691*57b5a4a6SAndroid Build Coastguard Worker 692*57b5a4a6SAndroid Build Coastguard Worker> In the following sections any kind of `Date` serializer would work. For example, if we want `Date` to be serialized 693*57b5a4a6SAndroid Build Coastguard Worker> as an object, we would use an approach from 694*57b5a4a6SAndroid Build Coastguard Worker> the [Composite serializer via surrogate](#composite-serializer-via-surrogate) section. 695*57b5a4a6SAndroid Build Coastguard Worker> See also [Deriving external serializer for another Kotlin class (experimental)](#deriving-external-serializer-for-another-kotlin-class-experimental) 696*57b5a4a6SAndroid Build Coastguard Worker> when you need to serialize a 3rd-party Kotlin class that could have been serializable, but is not. 697*57b5a4a6SAndroid Build Coastguard Worker 698*57b5a4a6SAndroid Build Coastguard Worker<!--- INCLUDE 699*57b5a4a6SAndroid Build Coastguard Workerimport java.util.Date 700*57b5a4a6SAndroid Build Coastguard Workerimport java.text.SimpleDateFormat 701*57b5a4a6SAndroid Build Coastguard Worker--> 702*57b5a4a6SAndroid Build Coastguard Worker 703*57b5a4a6SAndroid Build Coastguard Worker```kotlin 704*57b5a4a6SAndroid Build Coastguard Workerobject DateAsLongSerializer : KSerializer<Date> { 705*57b5a4a6SAndroid Build Coastguard Worker override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Date", PrimitiveKind.LONG) 706*57b5a4a6SAndroid Build Coastguard Worker override fun serialize(encoder: Encoder, value: Date) = encoder.encodeLong(value.time) 707*57b5a4a6SAndroid Build Coastguard Worker override fun deserialize(decoder: Decoder): Date = Date(decoder.decodeLong()) 708*57b5a4a6SAndroid Build Coastguard Worker} 709*57b5a4a6SAndroid Build Coastguard Worker``` 710*57b5a4a6SAndroid Build Coastguard Worker 711*57b5a4a6SAndroid Build Coastguard WorkerWe cannot bind the `DateAsLongSerializer` serializer to the `Date` class with the [`@Serializable`][Serializable] annotation 712*57b5a4a6SAndroid Build Coastguard Workerbecause we don't control the `Date` source code. There are several ways to work around that. 713*57b5a4a6SAndroid Build Coastguard Worker 714*57b5a4a6SAndroid Build Coastguard Worker### Passing a serializer manually 715*57b5a4a6SAndroid Build Coastguard Worker 716*57b5a4a6SAndroid Build Coastguard WorkerAll `encodeToXxx` and `decodeFromXxx` functions have an overload with the first serializer parameter. 717*57b5a4a6SAndroid Build Coastguard WorkerWhen a non-serializable class, like `Date`, is the top-level class being serialized, we can use those. 718*57b5a4a6SAndroid Build Coastguard Worker 719*57b5a4a6SAndroid Build Coastguard Worker```kotlin 720*57b5a4a6SAndroid Build Coastguard Workerfun main() { 721*57b5a4a6SAndroid Build Coastguard Worker val kotlin10ReleaseDate = SimpleDateFormat("yyyy-MM-ddX").parse("2016-02-15+00") 722*57b5a4a6SAndroid Build Coastguard Worker println(Json.encodeToString(DateAsLongSerializer, kotlin10ReleaseDate)) 723*57b5a4a6SAndroid Build Coastguard Worker} 724*57b5a4a6SAndroid Build Coastguard Worker``` 725*57b5a4a6SAndroid Build Coastguard Worker 726*57b5a4a6SAndroid Build Coastguard Worker> You can get the full code [here](../guide/example/example-serializer-14.kt). 727*57b5a4a6SAndroid Build Coastguard Worker 728*57b5a4a6SAndroid Build Coastguard Worker```text 729*57b5a4a6SAndroid Build Coastguard Worker1455494400000 730*57b5a4a6SAndroid Build Coastguard Worker``` 731*57b5a4a6SAndroid Build Coastguard Worker 732*57b5a4a6SAndroid Build Coastguard Worker<!--- TEST --> 733*57b5a4a6SAndroid Build Coastguard Worker 734*57b5a4a6SAndroid Build Coastguard Worker### Specifying serializer on a property 735*57b5a4a6SAndroid Build Coastguard Worker 736*57b5a4a6SAndroid Build Coastguard WorkerWhen a property of a non-serializable class, like `Date`, is serialized as part of a serializable class we must supply 737*57b5a4a6SAndroid Build Coastguard Workerits serializer or the code will not compile. This is accomplished using the [`@Serializable`][Serializable] annotation on the property. 738*57b5a4a6SAndroid Build Coastguard Worker 739*57b5a4a6SAndroid Build Coastguard Worker<!--- INCLUDE 740*57b5a4a6SAndroid Build Coastguard Workerimport java.util.Date 741*57b5a4a6SAndroid Build Coastguard Workerimport java.text.SimpleDateFormat 742*57b5a4a6SAndroid Build Coastguard Worker 743*57b5a4a6SAndroid Build Coastguard Workerobject DateAsLongSerializer : KSerializer<Date> { 744*57b5a4a6SAndroid Build Coastguard Worker override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Date", PrimitiveKind.LONG) 745*57b5a4a6SAndroid Build Coastguard Worker override fun serialize(encoder: Encoder, value: Date) = encoder.encodeLong(value.time) 746*57b5a4a6SAndroid Build Coastguard Worker override fun deserialize(decoder: Decoder): Date = Date(decoder.decodeLong()) 747*57b5a4a6SAndroid Build Coastguard Worker} 748*57b5a4a6SAndroid Build Coastguard Worker--> 749*57b5a4a6SAndroid Build Coastguard Worker 750*57b5a4a6SAndroid Build Coastguard Worker```kotlin 751*57b5a4a6SAndroid Build Coastguard Worker@Serializable 752*57b5a4a6SAndroid Build Coastguard Workerclass ProgrammingLanguage( 753*57b5a4a6SAndroid Build Coastguard Worker val name: String, 754*57b5a4a6SAndroid Build Coastguard Worker @Serializable(with = DateAsLongSerializer::class) 755*57b5a4a6SAndroid Build Coastguard Worker val stableReleaseDate: Date 756*57b5a4a6SAndroid Build Coastguard Worker) 757*57b5a4a6SAndroid Build Coastguard Worker 758*57b5a4a6SAndroid Build Coastguard Workerfun main() { 759*57b5a4a6SAndroid Build Coastguard Worker val data = ProgrammingLanguage("Kotlin", SimpleDateFormat("yyyy-MM-ddX").parse("2016-02-15+00")) 760*57b5a4a6SAndroid Build Coastguard Worker println(Json.encodeToString(data)) 761*57b5a4a6SAndroid Build Coastguard Worker} 762*57b5a4a6SAndroid Build Coastguard Worker``` 763*57b5a4a6SAndroid Build Coastguard Worker 764*57b5a4a6SAndroid Build Coastguard Worker> You can get the full code [here](../guide/example/example-serializer-15.kt). 765*57b5a4a6SAndroid Build Coastguard Worker 766*57b5a4a6SAndroid Build Coastguard WorkerThe `stableReleaseDate` property is serialized with the serialization strategy that we specified for it: 767*57b5a4a6SAndroid Build Coastguard Worker 768*57b5a4a6SAndroid Build Coastguard Worker```text 769*57b5a4a6SAndroid Build Coastguard Worker{"name":"Kotlin","stableReleaseDate":1455494400000} 770*57b5a4a6SAndroid Build Coastguard Worker``` 771*57b5a4a6SAndroid Build Coastguard Worker 772*57b5a4a6SAndroid Build Coastguard Worker<!--- TEST --> 773*57b5a4a6SAndroid Build Coastguard Worker 774*57b5a4a6SAndroid Build Coastguard Worker### Specifying serializer for a particular type 775*57b5a4a6SAndroid Build Coastguard Worker 776*57b5a4a6SAndroid Build Coastguard Worker[`@Serializable`][Serializable] annotation can also be applied directly to the types. 777*57b5a4a6SAndroid Build Coastguard WorkerThis is handy when a class that requires a custom serializer, such as `Date`, happens to be a generic type argument. 778*57b5a4a6SAndroid Build Coastguard WorkerThe most common use case for that is when you have a list of dates: 779*57b5a4a6SAndroid Build Coastguard Worker 780*57b5a4a6SAndroid Build Coastguard Worker<!--- INCLUDE 781*57b5a4a6SAndroid Build Coastguard Workerimport java.util.Date 782*57b5a4a6SAndroid Build Coastguard Workerimport java.text.SimpleDateFormat 783*57b5a4a6SAndroid Build Coastguard Worker 784*57b5a4a6SAndroid Build Coastguard Workerobject DateAsLongSerializer : KSerializer<Date> { 785*57b5a4a6SAndroid Build Coastguard Worker override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Date", PrimitiveKind.LONG) 786*57b5a4a6SAndroid Build Coastguard Worker override fun serialize(encoder: Encoder, value: Date) = encoder.encodeLong(value.time) 787*57b5a4a6SAndroid Build Coastguard Worker override fun deserialize(decoder: Decoder): Date = Date(decoder.decodeLong()) 788*57b5a4a6SAndroid Build Coastguard Worker} 789*57b5a4a6SAndroid Build Coastguard Worker--> 790*57b5a4a6SAndroid Build Coastguard Worker 791*57b5a4a6SAndroid Build Coastguard Worker```kotlin 792*57b5a4a6SAndroid Build Coastguard Worker@Serializable 793*57b5a4a6SAndroid Build Coastguard Workerclass ProgrammingLanguage( 794*57b5a4a6SAndroid Build Coastguard Worker val name: String, 795*57b5a4a6SAndroid Build Coastguard Worker val releaseDates: List<@Serializable(DateAsLongSerializer::class) Date> 796*57b5a4a6SAndroid Build Coastguard Worker) 797*57b5a4a6SAndroid Build Coastguard Worker 798*57b5a4a6SAndroid Build Coastguard Workerfun main() { 799*57b5a4a6SAndroid Build Coastguard Worker val df = SimpleDateFormat("yyyy-MM-ddX") 800*57b5a4a6SAndroid Build Coastguard Worker val data = ProgrammingLanguage("Kotlin", listOf(df.parse("2023-07-06+00"), df.parse("2023-04-25+00"), df.parse("2022-12-28+00"))) 801*57b5a4a6SAndroid Build Coastguard Worker println(Json.encodeToString(data)) 802*57b5a4a6SAndroid Build Coastguard Worker} 803*57b5a4a6SAndroid Build Coastguard Worker``` 804*57b5a4a6SAndroid Build Coastguard Worker 805*57b5a4a6SAndroid Build Coastguard Worker> You can get the full code [here](../guide/example/example-serializer-16.kt). 806*57b5a4a6SAndroid Build Coastguard Worker 807*57b5a4a6SAndroid Build Coastguard Worker```text 808*57b5a4a6SAndroid Build Coastguard Worker{"name":"Kotlin","releaseDates":[1688601600000,1682380800000,1672185600000]} 809*57b5a4a6SAndroid Build Coastguard Worker``` 810*57b5a4a6SAndroid Build Coastguard Worker 811*57b5a4a6SAndroid Build Coastguard Worker<!--- TEST --> 812*57b5a4a6SAndroid Build Coastguard Worker 813*57b5a4a6SAndroid Build Coastguard Worker### Specifying serializers for a file 814*57b5a4a6SAndroid Build Coastguard Worker 815*57b5a4a6SAndroid Build Coastguard WorkerA serializer for a specific type, like `Date`, can be specified for a whole source code file with the file-level 816*57b5a4a6SAndroid Build Coastguard Worker[UseSerializers] annotation at the beginning of the file. 817*57b5a4a6SAndroid Build Coastguard Worker 818*57b5a4a6SAndroid Build Coastguard Worker```kotlin 819*57b5a4a6SAndroid Build Coastguard Worker@file:UseSerializers(DateAsLongSerializer::class) 820*57b5a4a6SAndroid Build Coastguard Worker``` 821*57b5a4a6SAndroid Build Coastguard Worker 822*57b5a4a6SAndroid Build Coastguard Worker<!--- PREFIX --> 823*57b5a4a6SAndroid Build Coastguard Worker 824*57b5a4a6SAndroid Build Coastguard Worker<!--- INCLUDE 825*57b5a4a6SAndroid Build Coastguard Workerimport java.util.Date 826*57b5a4a6SAndroid Build Coastguard Workerimport java.text.SimpleDateFormat 827*57b5a4a6SAndroid Build Coastguard Worker 828*57b5a4a6SAndroid Build Coastguard Workerobject DateAsLongSerializer : KSerializer<Date> { 829*57b5a4a6SAndroid Build Coastguard Worker override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Date", PrimitiveKind.LONG) 830*57b5a4a6SAndroid Build Coastguard Worker override fun serialize(encoder: Encoder, value: Date) = encoder.encodeLong(value.time) 831*57b5a4a6SAndroid Build Coastguard Worker override fun deserialize(decoder: Decoder): Date = Date(decoder.decodeLong()) 832*57b5a4a6SAndroid Build Coastguard Worker} 833*57b5a4a6SAndroid Build Coastguard Worker--> 834*57b5a4a6SAndroid Build Coastguard Worker 835*57b5a4a6SAndroid Build Coastguard WorkerNow a `Date` property can be used in a serializable class without additional annotations. 836*57b5a4a6SAndroid Build Coastguard Worker 837*57b5a4a6SAndroid Build Coastguard Worker```kotlin 838*57b5a4a6SAndroid Build Coastguard Worker@Serializable 839*57b5a4a6SAndroid Build Coastguard Workerclass ProgrammingLanguage(val name: String, val stableReleaseDate: Date) 840*57b5a4a6SAndroid Build Coastguard Worker 841*57b5a4a6SAndroid Build Coastguard Workerfun main() { 842*57b5a4a6SAndroid Build Coastguard Worker val data = ProgrammingLanguage("Kotlin", SimpleDateFormat("yyyy-MM-ddX").parse("2016-02-15+00")) 843*57b5a4a6SAndroid Build Coastguard Worker println(Json.encodeToString(data)) 844*57b5a4a6SAndroid Build Coastguard Worker} 845*57b5a4a6SAndroid Build Coastguard Worker``` 846*57b5a4a6SAndroid Build Coastguard Worker> You can get the full code [here](../guide/example/example-serializer-17.kt). 847*57b5a4a6SAndroid Build Coastguard Worker 848*57b5a4a6SAndroid Build Coastguard Worker```text 849*57b5a4a6SAndroid Build Coastguard Worker{"name":"Kotlin","stableReleaseDate":1455494400000} 850*57b5a4a6SAndroid Build Coastguard Worker``` 851*57b5a4a6SAndroid Build Coastguard Worker 852*57b5a4a6SAndroid Build Coastguard Worker<!--- TEST --> 853*57b5a4a6SAndroid Build Coastguard Worker 854*57b5a4a6SAndroid Build Coastguard Worker### Specifying serializer globally using typealias 855*57b5a4a6SAndroid Build Coastguard Worker 856*57b5a4a6SAndroid Build Coastguard Workerkotlinx.serialization tends to be the always-explicit framework when it comes to serialization strategies: normally, 857*57b5a4a6SAndroid Build Coastguard Workerthey should be explicitly mentioned in `@Serializable` annotation. Therefore, we do not provide any kind of global serializer 858*57b5a4a6SAndroid Build Coastguard Workerconfiguration (except for [context serializer](#contextual-serialization) mentioned later). 859*57b5a4a6SAndroid Build Coastguard Worker 860*57b5a4a6SAndroid Build Coastguard WorkerHowever, in projects with a large number of files and classes, it may be too cumbersome to specify `@file:UseSerializers` 861*57b5a4a6SAndroid Build Coastguard Workerevery time, especially for classes like `Date` or `Instant` that have a fixed strategy of serialization across the project. 862*57b5a4a6SAndroid Build Coastguard WorkerFor such cases, it is possible to specify serializers using `typealias`es, as they preserve annotations, including serialization-related ones: 863*57b5a4a6SAndroid Build Coastguard Worker<!--- INCLUDE 864*57b5a4a6SAndroid Build Coastguard Workerimport java.util.Date 865*57b5a4a6SAndroid Build Coastguard Workerimport java.text.SimpleDateFormat 866*57b5a4a6SAndroid Build Coastguard Worker 867*57b5a4a6SAndroid Build Coastguard Workerobject DateAsLongSerializer : KSerializer<Date> { 868*57b5a4a6SAndroid Build Coastguard Worker override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("DateAsLong", PrimitiveKind.LONG) 869*57b5a4a6SAndroid Build Coastguard Worker override fun serialize(encoder: Encoder, value: Date) = encoder.encodeLong(value.time) 870*57b5a4a6SAndroid Build Coastguard Worker override fun deserialize(decoder: Decoder): Date = Date(decoder.decodeLong()) 871*57b5a4a6SAndroid Build Coastguard Worker} 872*57b5a4a6SAndroid Build Coastguard Worker 873*57b5a4a6SAndroid Build Coastguard Workerobject DateAsSimpleTextSerializer: KSerializer<Date> { 874*57b5a4a6SAndroid Build Coastguard Worker override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("DateAsSimpleText", PrimitiveKind.LONG) 875*57b5a4a6SAndroid Build Coastguard Worker private val format = SimpleDateFormat("yyyy-MM-dd") 876*57b5a4a6SAndroid Build Coastguard Worker override fun serialize(encoder: Encoder, value: Date) = encoder.encodeString(format.format(value)) 877*57b5a4a6SAndroid Build Coastguard Worker override fun deserialize(decoder: Decoder): Date = format.parse(decoder.decodeString()) 878*57b5a4a6SAndroid Build Coastguard Worker} 879*57b5a4a6SAndroid Build Coastguard Worker--> 880*57b5a4a6SAndroid Build Coastguard Worker 881*57b5a4a6SAndroid Build Coastguard Worker```kotlin 882*57b5a4a6SAndroid Build Coastguard Workertypealias DateAsLong = @Serializable(DateAsLongSerializer::class) Date 883*57b5a4a6SAndroid Build Coastguard Worker 884*57b5a4a6SAndroid Build Coastguard Workertypealias DateAsText = @Serializable(DateAsSimpleTextSerializer::class) Date 885*57b5a4a6SAndroid Build Coastguard Worker``` 886*57b5a4a6SAndroid Build Coastguard Worker 887*57b5a4a6SAndroid Build Coastguard WorkerUsing these new different types, it is possible to serialize a Date differently without additional annotations: 888*57b5a4a6SAndroid Build Coastguard Worker 889*57b5a4a6SAndroid Build Coastguard Worker```kotlin 890*57b5a4a6SAndroid Build Coastguard Worker@Serializable 891*57b5a4a6SAndroid Build Coastguard Workerclass ProgrammingLanguage(val stableReleaseDate: DateAsText, val lastReleaseTimestamp: DateAsLong) 892*57b5a4a6SAndroid Build Coastguard Worker 893*57b5a4a6SAndroid Build Coastguard Workerfun main() { 894*57b5a4a6SAndroid Build Coastguard Worker val format = SimpleDateFormat("yyyy-MM-ddX") 895*57b5a4a6SAndroid Build Coastguard Worker val data = ProgrammingLanguage(format.parse("2016-02-15+00"), format.parse("2022-07-07+00")) 896*57b5a4a6SAndroid Build Coastguard Worker println(Json.encodeToString(data)) 897*57b5a4a6SAndroid Build Coastguard Worker} 898*57b5a4a6SAndroid Build Coastguard Worker``` 899*57b5a4a6SAndroid Build Coastguard Worker 900*57b5a4a6SAndroid Build Coastguard Worker> You can get the full code [here](../guide/example/example-serializer-18.kt). 901*57b5a4a6SAndroid Build Coastguard Worker 902*57b5a4a6SAndroid Build Coastguard Worker```text 903*57b5a4a6SAndroid Build Coastguard Worker{"stableReleaseDate":"2016-02-15","lastReleaseTimestamp":1657152000000} 904*57b5a4a6SAndroid Build Coastguard Worker``` 905*57b5a4a6SAndroid Build Coastguard Worker 906*57b5a4a6SAndroid Build Coastguard Worker<!--- TEST --> 907*57b5a4a6SAndroid Build Coastguard Worker 908*57b5a4a6SAndroid Build Coastguard Worker### Custom serializers for a generic type 909*57b5a4a6SAndroid Build Coastguard Worker 910*57b5a4a6SAndroid Build Coastguard WorkerLet us take a look at the following example of the generic `Box<T>` class. 911*57b5a4a6SAndroid Build Coastguard WorkerIt is marked with `@Serializable(with = BoxSerializer::class)` as we plan to have a custom serialization 912*57b5a4a6SAndroid Build Coastguard Workerstrategy for it. 913*57b5a4a6SAndroid Build Coastguard Worker 914*57b5a4a6SAndroid Build Coastguard Worker```kotlin 915*57b5a4a6SAndroid Build Coastguard Worker@Serializable(with = BoxSerializer::class) 916*57b5a4a6SAndroid Build Coastguard Workerdata class Box<T>(val contents: T) 917*57b5a4a6SAndroid Build Coastguard Worker``` 918*57b5a4a6SAndroid Build Coastguard Worker 919*57b5a4a6SAndroid Build Coastguard WorkerAn implementation of [KSerializer] for a regular type is written as an `object`, as we saw in this chapter's 920*57b5a4a6SAndroid Build Coastguard Workerexamples for the `Color` type. A generic class serializer is instantiated with serializers 921*57b5a4a6SAndroid Build Coastguard Workerfor its generic parameters. We saw this in the [Plugin-generated generic serializer](#plugin-generated-generic-serializer) section. 922*57b5a4a6SAndroid Build Coastguard WorkerA custom serializer for a generic class must be a `class` with a constructor that accepts as many [KSerializer] 923*57b5a4a6SAndroid Build Coastguard Workerparameters as the type has generic parameters. Let us write a `Box<T>` serializer that erases itself during 924*57b5a4a6SAndroid Build Coastguard Workerserialization, delegating everything to the underlying serializer of its `data` property. 925*57b5a4a6SAndroid Build Coastguard Worker 926*57b5a4a6SAndroid Build Coastguard Worker```kotlin 927*57b5a4a6SAndroid Build Coastguard Workerclass BoxSerializer<T>(private val dataSerializer: KSerializer<T>) : KSerializer<Box<T>> { 928*57b5a4a6SAndroid Build Coastguard Worker override val descriptor: SerialDescriptor = dataSerializer.descriptor 929*57b5a4a6SAndroid Build Coastguard Worker override fun serialize(encoder: Encoder, value: Box<T>) = dataSerializer.serialize(encoder, value.contents) 930*57b5a4a6SAndroid Build Coastguard Worker override fun deserialize(decoder: Decoder) = Box(dataSerializer.deserialize(decoder)) 931*57b5a4a6SAndroid Build Coastguard Worker} 932*57b5a4a6SAndroid Build Coastguard Worker``` 933*57b5a4a6SAndroid Build Coastguard Worker 934*57b5a4a6SAndroid Build Coastguard WorkerNow we can serialize and deserialize `Box<Project>`. 935*57b5a4a6SAndroid Build Coastguard Worker 936*57b5a4a6SAndroid Build Coastguard Worker```kotlin 937*57b5a4a6SAndroid Build Coastguard Worker@Serializable 938*57b5a4a6SAndroid Build Coastguard Workerdata class Project(val name: String) 939*57b5a4a6SAndroid Build Coastguard Worker 940*57b5a4a6SAndroid Build Coastguard Workerfun main() { 941*57b5a4a6SAndroid Build Coastguard Worker val box = Box(Project("kotlinx.serialization")) 942*57b5a4a6SAndroid Build Coastguard Worker val string = Json.encodeToString(box) 943*57b5a4a6SAndroid Build Coastguard Worker println(string) 944*57b5a4a6SAndroid Build Coastguard Worker println(Json.decodeFromString<Box<Project>>(string)) 945*57b5a4a6SAndroid Build Coastguard Worker} 946*57b5a4a6SAndroid Build Coastguard Worker``` 947*57b5a4a6SAndroid Build Coastguard Worker 948*57b5a4a6SAndroid Build Coastguard Worker> You can get the full code [here](../guide/example/example-serializer-19.kt). 949*57b5a4a6SAndroid Build Coastguard Worker 950*57b5a4a6SAndroid Build Coastguard WorkerThe resulting JSON looks like the `Project` class was serialized directly. 951*57b5a4a6SAndroid Build Coastguard Worker 952*57b5a4a6SAndroid Build Coastguard Worker```text 953*57b5a4a6SAndroid Build Coastguard Worker{"name":"kotlinx.serialization"} 954*57b5a4a6SAndroid Build Coastguard WorkerBox(contents=Project(name=kotlinx.serialization)) 955*57b5a4a6SAndroid Build Coastguard Worker``` 956*57b5a4a6SAndroid Build Coastguard Worker 957*57b5a4a6SAndroid Build Coastguard Worker<!--- TEST --> 958*57b5a4a6SAndroid Build Coastguard Worker 959*57b5a4a6SAndroid Build Coastguard Worker### Format-specific serializers 960*57b5a4a6SAndroid Build Coastguard Worker 961*57b5a4a6SAndroid Build Coastguard WorkerThe above custom serializers worked in the same way for every format. However, there might be format-specific 962*57b5a4a6SAndroid Build Coastguard Workerfeatures that a serializer implementation would like to take advantage of. 963*57b5a4a6SAndroid Build Coastguard Worker 964*57b5a4a6SAndroid Build Coastguard Worker* The [Json transformations](json.md#json-transformations) section of the [Json](json.md) chapter provides examples 965*57b5a4a6SAndroid Build Coastguard Worker of serializers that utilize JSON-specific features. 966*57b5a4a6SAndroid Build Coastguard Worker 967*57b5a4a6SAndroid Build Coastguard Worker* A format implementation can have a format-specific representation for a type as explained 968*57b5a4a6SAndroid Build Coastguard Worker in the [Format-specific types](formats.md#format-specific-types) section of 969*57b5a4a6SAndroid Build Coastguard Worker the [Alternative and custom formats (experimental)](formats.md) chapter. 970*57b5a4a6SAndroid Build Coastguard Worker 971*57b5a4a6SAndroid Build Coastguard WorkerThis chapter proceeds with a generic approach to tweaking the serialization strategy based on the context. 972*57b5a4a6SAndroid Build Coastguard Worker 973*57b5a4a6SAndroid Build Coastguard Worker## Contextual serialization 974*57b5a4a6SAndroid Build Coastguard Worker 975*57b5a4a6SAndroid Build Coastguard WorkerAll the previous approaches to specifying custom serialization strategies were _static_, that is 976*57b5a4a6SAndroid Build Coastguard Workerfully defined at compile-time. The exception was the [Passing a serializer manually](#passing-a-serializer-manually) 977*57b5a4a6SAndroid Build Coastguard Workerapproach, but it worked only on a top-level object. You might need to change the serialization 978*57b5a4a6SAndroid Build Coastguard Workerstrategy for objects deep in the serialized object tree at run-time, with the strategy being selected in a context-dependent way. 979*57b5a4a6SAndroid Build Coastguard WorkerFor example, you might want to represent `java.util.Date` in JSON format as an ISO 8601 string or as a long integer 980*57b5a4a6SAndroid Build Coastguard Workerdepending on a version of a protocol you are serializing data for. This is called _contextual_ serialization, and it 981*57b5a4a6SAndroid Build Coastguard Workeris supported by a built-in [ContextualSerializer] class. Usually we don't have to use this serializer class explicitly—there 982*57b5a4a6SAndroid Build Coastguard Workeris the [Contextual] annotation providing a shortcut to 983*57b5a4a6SAndroid Build Coastguard Workerthe `@Serializable(with = ContextualSerializer::class)` annotation, 984*57b5a4a6SAndroid Build Coastguard Workeror the [UseContextualSerialization] annotation can be used at the file-level just like 985*57b5a4a6SAndroid Build Coastguard Workerthe [UseSerializers] annotation. Let's see an example utilizing the former. 986*57b5a4a6SAndroid Build Coastguard Worker 987*57b5a4a6SAndroid Build Coastguard Worker<!--- INCLUDE 988*57b5a4a6SAndroid Build Coastguard Workerimport java.util.Date 989*57b5a4a6SAndroid Build Coastguard Workerimport java.text.SimpleDateFormat 990*57b5a4a6SAndroid Build Coastguard Worker--> 991*57b5a4a6SAndroid Build Coastguard Worker 992*57b5a4a6SAndroid Build Coastguard Worker```kotlin 993*57b5a4a6SAndroid Build Coastguard Worker@Serializable 994*57b5a4a6SAndroid Build Coastguard Workerclass ProgrammingLanguage( 995*57b5a4a6SAndroid Build Coastguard Worker val name: String, 996*57b5a4a6SAndroid Build Coastguard Worker @Contextual 997*57b5a4a6SAndroid Build Coastguard Worker val stableReleaseDate: Date 998*57b5a4a6SAndroid Build Coastguard Worker) 999*57b5a4a6SAndroid Build Coastguard Worker``` 1000*57b5a4a6SAndroid Build Coastguard Worker 1001*57b5a4a6SAndroid Build Coastguard Worker<!--- INCLUDE 1002*57b5a4a6SAndroid Build Coastguard Worker 1003*57b5a4a6SAndroid Build Coastguard Workerfun main() { 1004*57b5a4a6SAndroid Build Coastguard Worker val data = ProgrammingLanguage("Kotlin", SimpleDateFormat("yyyy-MM-ddX").parse("2016-02-15+00")) 1005*57b5a4a6SAndroid Build Coastguard Worker println(Json.encodeToString(data)) 1006*57b5a4a6SAndroid Build Coastguard Worker} 1007*57b5a4a6SAndroid Build Coastguard Worker--> 1008*57b5a4a6SAndroid Build Coastguard Worker 1009*57b5a4a6SAndroid Build Coastguard WorkerTo actually serialize this class we must provide the corresponding context when calling the `encodeToXxx`/`decodeFromXxx` 1010*57b5a4a6SAndroid Build Coastguard Workerfunctions. Without it we'll get a "Serializer for class 'Date' is not found" exception. 1011*57b5a4a6SAndroid Build Coastguard Worker 1012*57b5a4a6SAndroid Build Coastguard Worker> See [here](../guide/example/example-serializer-20.kt) for an example that produces that exception. 1013*57b5a4a6SAndroid Build Coastguard Worker 1014*57b5a4a6SAndroid Build Coastguard Worker<!--- TEST LINES_START 1015*57b5a4a6SAndroid Build Coastguard WorkerException in thread "main" kotlinx.serialization.SerializationException: Serializer for class 'Date' is not found. 1016*57b5a4a6SAndroid Build Coastguard WorkerPlease ensure that class is marked as '@Serializable' and that the serialization compiler plugin is applied. 1017*57b5a4a6SAndroid Build Coastguard Worker--> 1018*57b5a4a6SAndroid Build Coastguard Worker 1019*57b5a4a6SAndroid Build Coastguard Worker<!--- INCLUDE 1020*57b5a4a6SAndroid Build Coastguard Workerimport kotlinx.serialization.modules.* 1021*57b5a4a6SAndroid Build Coastguard Workerimport java.util.Date 1022*57b5a4a6SAndroid Build Coastguard Workerimport java.text.SimpleDateFormat 1023*57b5a4a6SAndroid Build Coastguard Worker 1024*57b5a4a6SAndroid Build Coastguard Workerobject DateAsLongSerializer : KSerializer<Date> { 1025*57b5a4a6SAndroid Build Coastguard Worker override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Date", PrimitiveKind.LONG) 1026*57b5a4a6SAndroid Build Coastguard Worker override fun serialize(encoder: Encoder, value: Date) = encoder.encodeLong(value.time) 1027*57b5a4a6SAndroid Build Coastguard Worker override fun deserialize(decoder: Decoder): Date = Date(decoder.decodeLong()) 1028*57b5a4a6SAndroid Build Coastguard Worker} 1029*57b5a4a6SAndroid Build Coastguard Worker 1030*57b5a4a6SAndroid Build Coastguard Worker@Serializable 1031*57b5a4a6SAndroid Build Coastguard Workerclass ProgrammingLanguage( 1032*57b5a4a6SAndroid Build Coastguard Worker val name: String, 1033*57b5a4a6SAndroid Build Coastguard Worker @Contextual 1034*57b5a4a6SAndroid Build Coastguard Worker val stableReleaseDate: Date 1035*57b5a4a6SAndroid Build Coastguard Worker) 1036*57b5a4a6SAndroid Build Coastguard Worker--> 1037*57b5a4a6SAndroid Build Coastguard Worker 1038*57b5a4a6SAndroid Build Coastguard Worker### Serializers module 1039*57b5a4a6SAndroid Build Coastguard Worker 1040*57b5a4a6SAndroid Build Coastguard WorkerTo provide a context, we define a [SerializersModule] instance that describes which serializers shall be used 1041*57b5a4a6SAndroid Build Coastguard Workerat run-time to serialize which contextually-serializable classes. This is done using the 1042*57b5a4a6SAndroid Build Coastguard Worker[SerializersModule {}][SerializersModule()] builder function, which provides the [SerializersModuleBuilder] DSL to 1043*57b5a4a6SAndroid Build Coastguard Workerregister serializers. In the below example we use the [contextual][_contextual] function with the serializer. The corresponding 1044*57b5a4a6SAndroid Build Coastguard Workerclass this serializer is defined for is fetched automatically via the `reified` type parameter. 1045*57b5a4a6SAndroid Build Coastguard Worker 1046*57b5a4a6SAndroid Build Coastguard Worker```kotlin 1047*57b5a4a6SAndroid Build Coastguard Workerprivate val module = SerializersModule { 1048*57b5a4a6SAndroid Build Coastguard Worker contextual(DateAsLongSerializer) 1049*57b5a4a6SAndroid Build Coastguard Worker} 1050*57b5a4a6SAndroid Build Coastguard Worker``` 1051*57b5a4a6SAndroid Build Coastguard Worker 1052*57b5a4a6SAndroid Build Coastguard WorkerNext we create an instance of the [Json] format with this module using the 1053*57b5a4a6SAndroid Build Coastguard Worker[Json {}][Json()] builder function and the [serializersModule][JsonBuilder.serializersModule] property. 1054*57b5a4a6SAndroid Build Coastguard Worker 1055*57b5a4a6SAndroid Build Coastguard Worker> Details on custom JSON configurations can be found in 1056*57b5a4a6SAndroid Build Coastguard Worker> the [JSON configuration](json.md#json-configuration) section. 1057*57b5a4a6SAndroid Build Coastguard Worker 1058*57b5a4a6SAndroid Build Coastguard Worker```kotlin 1059*57b5a4a6SAndroid Build Coastguard Workerval format = Json { serializersModule = module } 1060*57b5a4a6SAndroid Build Coastguard Worker``` 1061*57b5a4a6SAndroid Build Coastguard Worker 1062*57b5a4a6SAndroid Build Coastguard WorkerNow we can serialize our data with this `format`. 1063*57b5a4a6SAndroid Build Coastguard Worker 1064*57b5a4a6SAndroid Build Coastguard Worker```kotlin 1065*57b5a4a6SAndroid Build Coastguard Workerfun main() { 1066*57b5a4a6SAndroid Build Coastguard Worker val data = ProgrammingLanguage("Kotlin", SimpleDateFormat("yyyy-MM-ddX").parse("2016-02-15+00")) 1067*57b5a4a6SAndroid Build Coastguard Worker println(format.encodeToString(data)) 1068*57b5a4a6SAndroid Build Coastguard Worker} 1069*57b5a4a6SAndroid Build Coastguard Worker``` 1070*57b5a4a6SAndroid Build Coastguard Worker 1071*57b5a4a6SAndroid Build Coastguard Worker> You can get the full code [here](../guide/example/example-serializer-21.kt). 1072*57b5a4a6SAndroid Build Coastguard Worker```text 1073*57b5a4a6SAndroid Build Coastguard Worker{"name":"Kotlin","stableReleaseDate":1455494400000} 1074*57b5a4a6SAndroid Build Coastguard Worker``` 1075*57b5a4a6SAndroid Build Coastguard Worker 1076*57b5a4a6SAndroid Build Coastguard Worker<!--- TEST --> 1077*57b5a4a6SAndroid Build Coastguard Worker 1078*57b5a4a6SAndroid Build Coastguard Worker### Contextual serialization and generic classes 1079*57b5a4a6SAndroid Build Coastguard Worker 1080*57b5a4a6SAndroid Build Coastguard WorkerIn the previous section we saw that we can register serializer instance in the module for a class we want to serialize contextually. 1081*57b5a4a6SAndroid Build Coastguard WorkerWe also know that [serializers for generic classes have constructor parameters](#custom-serializers-for-a-generic-type) — type arguments serializers. 1082*57b5a4a6SAndroid Build Coastguard WorkerIt means that we can't use one serializer instance for a class if this class is generic: 1083*57b5a4a6SAndroid Build Coastguard Worker 1084*57b5a4a6SAndroid Build Coastguard Worker```kotlin 1085*57b5a4a6SAndroid Build Coastguard Workerval incorrectModule = SerializersModule { 1086*57b5a4a6SAndroid Build Coastguard Worker // Can serialize only Box<Int>, but not Box<String> or others 1087*57b5a4a6SAndroid Build Coastguard Worker contextual(BoxSerializer(Int.serializer())) 1088*57b5a4a6SAndroid Build Coastguard Worker} 1089*57b5a4a6SAndroid Build Coastguard Worker``` 1090*57b5a4a6SAndroid Build Coastguard Worker 1091*57b5a4a6SAndroid Build Coastguard WorkerFor cases when one want to serialize contextually a generic class, it is possible to register provider in the module: 1092*57b5a4a6SAndroid Build Coastguard Worker 1093*57b5a4a6SAndroid Build Coastguard Worker```kotlin 1094*57b5a4a6SAndroid Build Coastguard Workerval correctModule = SerializersModule { 1095*57b5a4a6SAndroid Build Coastguard Worker // args[0] contains Int.serializer() or String.serializer(), depending on the usage 1096*57b5a4a6SAndroid Build Coastguard Worker contextual(Box::class) { args -> BoxSerializer(args[0]) } 1097*57b5a4a6SAndroid Build Coastguard Worker} 1098*57b5a4a6SAndroid Build Coastguard Worker``` 1099*57b5a4a6SAndroid Build Coastguard Worker 1100*57b5a4a6SAndroid Build Coastguard Worker<!--- CLEAR --> 1101*57b5a4a6SAndroid Build Coastguard Worker 1102*57b5a4a6SAndroid Build Coastguard Worker> Additional details on serialization modules are given in 1103*57b5a4a6SAndroid Build Coastguard Worker> the [Merging library serializers modules](polymorphism.md#merging-library-serializers-modules) section of 1104*57b5a4a6SAndroid Build Coastguard Worker> the [Polymorphism](polymorphism.md) chapter. 1105*57b5a4a6SAndroid Build Coastguard Worker 1106*57b5a4a6SAndroid Build Coastguard Worker## Deriving external serializer for another Kotlin class (experimental) 1107*57b5a4a6SAndroid Build Coastguard Worker 1108*57b5a4a6SAndroid Build Coastguard WorkerIf a 3rd-party class to be serialized is a Kotlin class with a properties-only primary constructor, a kind of 1109*57b5a4a6SAndroid Build Coastguard Workerclass which could have been made `@Serializable`, then you can generate an _external_ serializer for it 1110*57b5a4a6SAndroid Build Coastguard Workerusing the [Serializer] annotation on an object with the [`forClass`][Serializer.forClass] property. 1111*57b5a4a6SAndroid Build Coastguard Worker 1112*57b5a4a6SAndroid Build Coastguard Worker```kotlin 1113*57b5a4a6SAndroid Build Coastguard Worker// NOT @Serializable 1114*57b5a4a6SAndroid Build Coastguard Workerclass Project(val name: String, val language: String) 1115*57b5a4a6SAndroid Build Coastguard Worker 1116*57b5a4a6SAndroid Build Coastguard Worker@Serializer(forClass = Project::class) 1117*57b5a4a6SAndroid Build Coastguard Workerobject ProjectSerializer 1118*57b5a4a6SAndroid Build Coastguard Worker``` 1119*57b5a4a6SAndroid Build Coastguard Worker 1120*57b5a4a6SAndroid Build Coastguard WorkerYou must bind this serializer to a class using one of the approaches explained in this chapter. We'll 1121*57b5a4a6SAndroid Build Coastguard Workerfollow the [Passing a serializer manually](#passing-a-serializer-manually) approach for this example. 1122*57b5a4a6SAndroid Build Coastguard Worker 1123*57b5a4a6SAndroid Build Coastguard Worker```kotlin 1124*57b5a4a6SAndroid Build Coastguard Workerfun main() { 1125*57b5a4a6SAndroid Build Coastguard Worker val data = Project("kotlinx.serialization", "Kotlin") 1126*57b5a4a6SAndroid Build Coastguard Worker println(Json.encodeToString(ProjectSerializer, data)) 1127*57b5a4a6SAndroid Build Coastguard Worker} 1128*57b5a4a6SAndroid Build Coastguard Worker``` 1129*57b5a4a6SAndroid Build Coastguard Worker 1130*57b5a4a6SAndroid Build Coastguard Worker> You can get the full code [here](../guide/example/example-serializer-22.kt). 1131*57b5a4a6SAndroid Build Coastguard Worker 1132*57b5a4a6SAndroid Build Coastguard WorkerThis gets all the `Project` properties serialized: 1133*57b5a4a6SAndroid Build Coastguard Worker 1134*57b5a4a6SAndroid Build Coastguard Worker```text 1135*57b5a4a6SAndroid Build Coastguard Worker{"name":"kotlinx.serialization","language":"Kotlin"} 1136*57b5a4a6SAndroid Build Coastguard Worker``` 1137*57b5a4a6SAndroid Build Coastguard Worker 1138*57b5a4a6SAndroid Build Coastguard Worker<!--- TEST --> 1139*57b5a4a6SAndroid Build Coastguard Worker 1140*57b5a4a6SAndroid Build Coastguard Worker### External serialization uses properties 1141*57b5a4a6SAndroid Build Coastguard Worker 1142*57b5a4a6SAndroid Build Coastguard WorkerAs we saw earlier, the regular `@Serializable` annotation creates a serializer so that 1143*57b5a4a6SAndroid Build Coastguard Worker[Backing fields are serialized](basic-serialization.md#backing-fields-are-serialized). _External_ serialization using 1144*57b5a4a6SAndroid Build Coastguard Worker`Serializer(forClass = ...)` has no access to backing fields and works differently. 1145*57b5a4a6SAndroid Build Coastguard WorkerIt serializes only _accessible_ properties that have setters or are part of the primary constructor. 1146*57b5a4a6SAndroid Build Coastguard WorkerThe following example shows this. 1147*57b5a4a6SAndroid Build Coastguard Worker 1148*57b5a4a6SAndroid Build Coastguard Worker```kotlin 1149*57b5a4a6SAndroid Build Coastguard Worker// NOT @Serializable, will use external serializer 1150*57b5a4a6SAndroid Build Coastguard Workerclass Project( 1151*57b5a4a6SAndroid Build Coastguard Worker // val in a primary constructor -- serialized 1152*57b5a4a6SAndroid Build Coastguard Worker val name: String 1153*57b5a4a6SAndroid Build Coastguard Worker) { 1154*57b5a4a6SAndroid Build Coastguard Worker var stars: Int = 0 // property with getter & setter -- serialized 1155*57b5a4a6SAndroid Build Coastguard Worker 1156*57b5a4a6SAndroid Build Coastguard Worker val path: String // getter only -- not serialized 1157*57b5a4a6SAndroid Build Coastguard Worker get() = "kotlin/$name" 1158*57b5a4a6SAndroid Build Coastguard Worker 1159*57b5a4a6SAndroid Build Coastguard Worker private var locked: Boolean = false // private, not accessible -- not serialized 1160*57b5a4a6SAndroid Build Coastguard Worker} 1161*57b5a4a6SAndroid Build Coastguard Worker 1162*57b5a4a6SAndroid Build Coastguard Worker@Serializer(forClass = Project::class) 1163*57b5a4a6SAndroid Build Coastguard Workerobject ProjectSerializer 1164*57b5a4a6SAndroid Build Coastguard Worker 1165*57b5a4a6SAndroid Build Coastguard Workerfun main() { 1166*57b5a4a6SAndroid Build Coastguard Worker val data = Project("kotlinx.serialization").apply { stars = 9000 } 1167*57b5a4a6SAndroid Build Coastguard Worker println(Json.encodeToString(ProjectSerializer, data)) 1168*57b5a4a6SAndroid Build Coastguard Worker} 1169*57b5a4a6SAndroid Build Coastguard Worker``` 1170*57b5a4a6SAndroid Build Coastguard Worker 1171*57b5a4a6SAndroid Build Coastguard Worker> You can get the full code [here](../guide/example/example-serializer-23.kt). 1172*57b5a4a6SAndroid Build Coastguard Worker 1173*57b5a4a6SAndroid Build Coastguard WorkerThe output is shown below. 1174*57b5a4a6SAndroid Build Coastguard Worker 1175*57b5a4a6SAndroid Build Coastguard Worker```text 1176*57b5a4a6SAndroid Build Coastguard Worker{"name":"kotlinx.serialization","stars":9000} 1177*57b5a4a6SAndroid Build Coastguard Worker``` 1178*57b5a4a6SAndroid Build Coastguard Worker 1179*57b5a4a6SAndroid Build Coastguard Worker<!--- TEST --> 1180*57b5a4a6SAndroid Build Coastguard Worker 1181*57b5a4a6SAndroid Build Coastguard Worker--- 1182*57b5a4a6SAndroid Build Coastguard Worker 1183*57b5a4a6SAndroid Build Coastguard WorkerThe next chapter covers [Polymorphism](polymorphism.md). 1184*57b5a4a6SAndroid Build Coastguard Worker 1185*57b5a4a6SAndroid Build Coastguard Worker<!-- Java references --> 1186*57b5a4a6SAndroid Build Coastguard Worker[java.util.Date]: https://docs.oracle.com/javase/8/docs/api/java/util/Date.html 1187*57b5a4a6SAndroid Build Coastguard Worker 1188*57b5a4a6SAndroid Build Coastguard Worker<!--- MODULE /kotlinx-serialization-core --> 1189*57b5a4a6SAndroid Build Coastguard Worker<!--- INDEX kotlinx-serialization-core/kotlinx.serialization --> 1190*57b5a4a6SAndroid Build Coastguard Worker 1191*57b5a4a6SAndroid Build Coastguard Worker[Serializable]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serializable/index.html 1192*57b5a4a6SAndroid Build Coastguard Worker[KSerializer]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-k-serializer/index.html 1193*57b5a4a6SAndroid Build Coastguard Worker[KSerializer.descriptor]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-k-serializer/descriptor.html 1194*57b5a4a6SAndroid Build Coastguard Worker[SerializationStrategy.serialize]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serialization-strategy/serialize.html 1195*57b5a4a6SAndroid Build Coastguard Worker[SerializationStrategy]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serialization-strategy/index.html 1196*57b5a4a6SAndroid Build Coastguard Worker[DeserializationStrategy.deserialize]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-deserialization-strategy/deserialize.html 1197*57b5a4a6SAndroid Build Coastguard Worker[DeserializationStrategy]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-deserialization-strategy/index.html 1198*57b5a4a6SAndroid Build Coastguard Worker[Serializable.with]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serializable/with.html 1199*57b5a4a6SAndroid Build Coastguard Worker[SerialName]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serial-name/index.html 1200*57b5a4a6SAndroid Build Coastguard Worker[UseSerializers]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-use-serializers/index.html 1201*57b5a4a6SAndroid Build Coastguard Worker[ContextualSerializer]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-contextual-serializer/index.html 1202*57b5a4a6SAndroid Build Coastguard Worker[Contextual]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-contextual/index.html 1203*57b5a4a6SAndroid Build Coastguard Worker[UseContextualSerialization]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-use-contextual-serialization/index.html 1204*57b5a4a6SAndroid Build Coastguard Worker[Serializer]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serializer/index.html 1205*57b5a4a6SAndroid Build Coastguard Worker[Serializer.forClass]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serializer/for-class.html 1206*57b5a4a6SAndroid Build Coastguard Worker 1207*57b5a4a6SAndroid Build Coastguard Worker<!--- INDEX kotlinx-serialization-core/kotlinx.serialization.builtins --> 1208*57b5a4a6SAndroid Build Coastguard Worker 1209*57b5a4a6SAndroid Build Coastguard Worker[ListSerializer()]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.builtins/-list-serializer.html 1210*57b5a4a6SAndroid Build Coastguard Worker[SetSerializer()]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.builtins/-set-serializer.html 1211*57b5a4a6SAndroid Build Coastguard Worker[MapSerializer()]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.builtins/-map-serializer.html 1212*57b5a4a6SAndroid Build Coastguard Worker 1213*57b5a4a6SAndroid Build Coastguard Worker<!--- INDEX kotlinx-serialization-core/kotlinx.serialization.encoding --> 1214*57b5a4a6SAndroid Build Coastguard Worker 1215*57b5a4a6SAndroid Build Coastguard Worker[Encoder]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/index.html 1216*57b5a4a6SAndroid Build Coastguard Worker[Encoder.encodeString]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/encode-string.html 1217*57b5a4a6SAndroid Build Coastguard Worker[Decoder]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-decoder/index.html 1218*57b5a4a6SAndroid Build Coastguard Worker[Decoder.decodeString]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-decoder/decode-string.html 1219*57b5a4a6SAndroid Build Coastguard Worker[Encoder.encodeSerializableValue]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-encoder/encode-serializable-value.html 1220*57b5a4a6SAndroid Build Coastguard Worker[Decoder.decodeSerializableValue]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-decoder/decode-serializable-value.html 1221*57b5a4a6SAndroid Build Coastguard Worker[encodeStructure]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/encode-structure.html 1222*57b5a4a6SAndroid Build Coastguard Worker[CompositeEncoder]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-composite-encoder/index.html 1223*57b5a4a6SAndroid Build Coastguard Worker[decodeStructure]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/decode-structure.html 1224*57b5a4a6SAndroid Build Coastguard Worker[CompositeDecoder]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-composite-decoder/index.html 1225*57b5a4a6SAndroid Build Coastguard Worker[CompositeDecoder.decodeElementIndex]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-composite-decoder/decode-element-index.html 1226*57b5a4a6SAndroid Build Coastguard Worker[CompositeDecoder.decodeIntElement]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-composite-decoder/decode-int-element.html 1227*57b5a4a6SAndroid Build Coastguard Worker[CompositeDecoder.decodeSequentially]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.encoding/-composite-decoder/decode-sequentially.html 1228*57b5a4a6SAndroid Build Coastguard Worker 1229*57b5a4a6SAndroid Build Coastguard Worker<!--- INDEX kotlinx-serialization-core/kotlinx.serialization.descriptors --> 1230*57b5a4a6SAndroid Build Coastguard Worker 1231*57b5a4a6SAndroid Build Coastguard Worker[PrimitiveSerialDescriptor()]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.descriptors/-primitive-serial-descriptor.html 1232*57b5a4a6SAndroid Build Coastguard Worker[PrimitiveKind]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.descriptors/-primitive-kind/index.html 1233*57b5a4a6SAndroid Build Coastguard Worker[SerialDescriptor]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.descriptors/-serial-descriptor/index.html 1234*57b5a4a6SAndroid Build Coastguard Worker[buildClassSerialDescriptor]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.descriptors/build-class-serial-descriptor.html 1235*57b5a4a6SAndroid Build Coastguard Worker[ClassSerialDescriptorBuilder.element]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.descriptors/element.html 1236*57b5a4a6SAndroid Build Coastguard Worker[SerialKind]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.descriptors/-serial-kind/index.html 1237*57b5a4a6SAndroid Build Coastguard Worker 1238*57b5a4a6SAndroid Build Coastguard Worker<!--- INDEX kotlinx-serialization-core/kotlinx.serialization.modules --> 1239*57b5a4a6SAndroid Build Coastguard Worker 1240*57b5a4a6SAndroid Build Coastguard Worker[SerializersModule]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.modules/-serializers-module/index.html 1241*57b5a4a6SAndroid Build Coastguard Worker[SerializersModule()]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.modules/-serializers-module.html 1242*57b5a4a6SAndroid Build Coastguard Worker[SerializersModuleBuilder]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.modules/-serializers-module-builder/index.html 1243*57b5a4a6SAndroid Build Coastguard Worker[_contextual]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.modules/contextual.html 1244*57b5a4a6SAndroid Build Coastguard Worker 1245*57b5a4a6SAndroid Build Coastguard Worker<!--- MODULE /kotlinx-serialization-json --> 1246*57b5a4a6SAndroid Build Coastguard Worker<!--- INDEX kotlinx-serialization-json/kotlinx.serialization.json --> 1247*57b5a4a6SAndroid Build Coastguard Worker 1248*57b5a4a6SAndroid Build Coastguard Worker[Json]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/index.html 1249*57b5a4a6SAndroid Build Coastguard Worker[Json()]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json.html 1250*57b5a4a6SAndroid Build Coastguard Worker[JsonBuilder.serializersModule]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json-builder/serializers-module.html 1251*57b5a4a6SAndroid Build Coastguard Worker 1252*57b5a4a6SAndroid Build Coastguard Worker<!--- END --> 1253*57b5a4a6SAndroid Build Coastguard Worker 1254