xref: /aosp_15_r20/external/kotlinx.serialization/docs/polymorphism.md (revision 57b5a4a64c534cf7f27ac9427ceab07f3d8ed3d8)
1<!--- TEST_NAME PolymorphismTest -->
2
3# Polymorphism
4
5This is the fourth chapter of the [Kotlin Serialization Guide](serialization-guide.md).
6In this chapter we'll see how Kotlin Serialization deals with polymorphic class hierarchies.
7
8**Table of contents**
9
10<!--- TOC -->
11
12* [Closed polymorphism](#closed-polymorphism)
13  * [Static types](#static-types)
14  * [Designing serializable hierarchy](#designing-serializable-hierarchy)
15  * [Sealed classes](#sealed-classes)
16  * [Custom subclass serial name](#custom-subclass-serial-name)
17  * [Concrete properties in a base class](#concrete-properties-in-a-base-class)
18  * [Objects](#objects)
19* [Open polymorphism](#open-polymorphism)
20  * [Registered subclasses](#registered-subclasses)
21  * [Serializing interfaces](#serializing-interfaces)
22  * [Property of an interface type](#property-of-an-interface-type)
23  * [Static parent type lookup for polymorphism](#static-parent-type-lookup-for-polymorphism)
24  * [Explicitly marking polymorphic class properties](#explicitly-marking-polymorphic-class-properties)
25  * [Registering multiple superclasses](#registering-multiple-superclasses)
26  * [Polymorphism and generic classes](#polymorphism-and-generic-classes)
27  * [Merging library serializers modules](#merging-library-serializers-modules)
28  * [Default polymorphic type handler for deserialization](#default-polymorphic-type-handler-for-deserialization)
29  * [Default polymorphic type handler for serialization](#default-polymorphic-type-handler-for-serialization)
30
31<!--- END -->
32
33<!--- INCLUDE .*-poly-.*
34import kotlinx.serialization.*
35import kotlinx.serialization.json.*
36-->
37
38## Closed polymorphism
39
40Let us start with basic introduction to polymorphism.
41
42### Static types
43
44Kotlin Serialization is fully static with respect to types by default. The structure of encoded objects is determined
45by *compile-time* types of objects. Let's examine this aspect in more detail and learn how
46to serialize polymorphic data structures, where the type of data is determined at runtime.
47
48To show the static nature of Kotlin Serialization let us make the following setup. An `open class Project`
49has just the `name` property, while its derived `class OwnedProject` adds an `owner` property.
50In the below example, we serialize `data` variable with a static type of
51`Project` that is initialized with an instance of `OwnedProject` at runtime.
52
53```kotlin
54@Serializable
55open class Project(val name: String)
56
57class OwnedProject(name: String, val owner: String) : Project(name)
58
59fun main() {
60    val data: Project = OwnedProject("kotlinx.coroutines", "kotlin")
61    println(Json.encodeToString(data))
62}
63```
64
65> You can get the full code [here](../guide/example/example-poly-01.kt).
66
67Despite the runtime type of `OwnedProject`, only the `Project` class properties are getting serialized.
68
69```text
70{"name":"kotlinx.coroutines"}
71```
72
73<!--- TEST -->
74
75Let's change the compile-time type of `data` to `OwnedProject`.
76
77```kotlin
78@Serializable
79open class Project(val name: String)
80
81class OwnedProject(name: String, val owner: String) : Project(name)
82
83fun main() {
84    val data = OwnedProject("kotlinx.coroutines", "kotlin")
85    println(Json.encodeToString(data))
86}
87```
88
89> You can get the full code [here](../guide/example/example-poly-02.kt).
90
91We get an error, because the `OwnedProject` class is not serializable.
92
93```text
94Exception in thread "main" kotlinx.serialization.SerializationException: Serializer for class 'OwnedProject' is not found.
95Please ensure that class is marked as '@Serializable' and that the serialization compiler plugin is applied.
96```
97
98<!--- TEST LINES_START -->
99
100### Designing serializable hierarchy
101
102We cannot simply mark `OwnedProject` from the previous example as `@Serializable`. It does not compile,
103running into the [constructor properties requirement](basic-serialization.md#constructor-properties-requirement).
104To make hierarchy of classes serializable, the properties in the parent class have to be marked `abstract`,
105making the `Project` class `abstract`, too.
106
107```kotlin
108@Serializable
109abstract class Project {
110    abstract val name: String
111}
112
113class OwnedProject(override val name: String, val owner: String) : Project()
114
115fun main() {
116    val data: Project = OwnedProject("kotlinx.coroutines", "kotlin")
117    println(Json.encodeToString(data))
118}
119```
120
121> You can get the full code [here](../guide/example/example-poly-03.kt).
122
123This is close to the best design for a serializable hierarchy of classes, but running it produces the following error:
124
125```text
126Exception in thread "main" kotlinx.serialization.SerializationException: Serializer for subclass 'OwnedProject' is not found in the polymorphic scope of 'Project'.
127Check if class with serial name 'OwnedProject' exists and serializer is registered in a corresponding SerializersModule.
128To be registered automatically, class 'OwnedProject' has to be '@Serializable', and the base class 'Project' has to be sealed and '@Serializable'.
129```
130
131<!--- TEST LINES_START -->
132
133### Sealed classes
134
135The most straightforward way to use serialization with a polymorphic hierarchy is to mark the base class `sealed`.
136_All_ subclasses of a sealed class must be explicitly marked as `@Serializable`.
137
138```kotlin
139@Serializable
140sealed class Project {
141    abstract val name: String
142}
143
144@Serializable
145class OwnedProject(override val name: String, val owner: String) : Project()
146
147fun main() {
148    val data: Project = OwnedProject("kotlinx.coroutines", "kotlin")
149    println(Json.encodeToString(data)) // Serializing data of compile-time type Project
150}
151```
152
153> You can get the full code [here](../guide/example/example-poly-04.kt).
154
155Now we can see a default way to represent polymorphism in JSON.
156A `type` key is added to the resulting JSON object as a _discriminator_.
157
158```text
159{"type":"example.examplePoly04.OwnedProject","name":"kotlinx.coroutines","owner":"kotlin"}
160```
161
162<!--- TEST -->
163
164Pay attention to the small, but very important detail in the above example that is related to [Static types](#static-types):
165the `val data` property has a compile-time type of `Project`, even though its run-time type is `OwnedProject`.
166When serializing polymorphic class hierarchies you must ensure that the compile-time type of the serialized object
167is a polymorphic one, not a concrete one.
168
169Let us see what happens if the example is slightly changed, so that the compile-time of the object that is being
170serialized is `OwnedProject` (the same as its run-time type).
171
172```kotlin
173@Serializable
174sealed class Project {
175    abstract val name: String
176}
177
178@Serializable
179class OwnedProject(override val name: String, val owner: String) : Project()
180
181fun main() {
182    val data = OwnedProject("kotlinx.coroutines", "kotlin") // data: OwnedProject here
183    println(Json.encodeToString(data)) // Serializing data of compile-time type OwnedProject
184}
185```
186
187> You can get the full code [here](../guide/example/example-poly-05.kt).
188
189The type of `OwnedProject` is concrete and is not polymorphic, thus the `type`
190discriminator property is not emitted into the resulting JSON.
191
192```text
193{"name":"kotlinx.coroutines","owner":"kotlin"}
194```
195
196<!--- TEST -->
197
198In general, Kotlin Serialization is designed to work correctly only when the compile-time type used during serialization
199is the same one as the compile-time type used during deserialization. You can always specify the type explicitly
200when calling serialization functions. The previous example can be corrected to use `Project` type for serialization
201by calling `Json.encodeToString<Project>(data)`.
202
203### Custom subclass serial name
204
205A value of the `type` key is a fully qualified class name by default. We can put [SerialName] annotation onto
206the corresponding class to change it.
207
208```kotlin
209@Serializable
210sealed class Project {
211    abstract val name: String
212}
213
214@Serializable
215@SerialName("owned")
216class OwnedProject(override val name: String, val owner: String) : Project()
217
218fun main() {
219    val data: Project = OwnedProject("kotlinx.coroutines", "kotlin")
220    println(Json.encodeToString(data))
221}
222```
223
224> You can get the full code [here](../guide/example/example-poly-06.kt).
225
226This way we can have a stable _serial name_ that is not affected by the class's name in the source code.
227
228```text
229{"type":"owned","name":"kotlinx.coroutines","owner":"kotlin"}
230```
231
232<!--- TEST -->
233
234> In addition to that, JSON can be configured to use a different key name for the class discriminator.
235> You can find an example in the [Class discriminator for polymorphism](json.md#class-discriminator-for-polymorphism) section.
236
237### Concrete properties in a base class
238
239A base class in a sealed hierarchy can have properties with backing fields.
240
241```kotlin
242@Serializable
243sealed class Project {
244    abstract val name: String
245    var status = "open"
246}
247
248@Serializable
249@SerialName("owned")
250class OwnedProject(override val name: String, val owner: String) : Project()
251
252fun main() {
253    val json = Json { encodeDefaults = true } // "status" will be skipped otherwise
254    val data: Project = OwnedProject("kotlinx.coroutines", "kotlin")
255    println(json.encodeToString(data))
256}
257```
258
259> You can get the full code [here](../guide/example/example-poly-07.kt).
260
261The properties of the superclass are serialized before the properties of the subclass.
262
263```text
264{"type":"owned","status":"open","name":"kotlinx.coroutines","owner":"kotlin"}
265```
266
267<!--- TEST -->
268
269### Objects
270
271Sealed hierarchies can have objects as their subclasses and they also need to be marked as `@Serializable`.
272Let's take a different example with a hierarchy of `Response` classes.
273
274```kotlin
275@Serializable
276sealed class Response
277
278@Serializable
279object EmptyResponse : Response()
280
281@Serializable
282class TextResponse(val text: String) : Response()
283```
284
285Let us serialize a list of different responses.
286
287```kotlin
288fun main() {
289    val list = listOf(EmptyResponse, TextResponse("OK"))
290    println(Json.encodeToString(list))
291}
292```
293
294> You can get the full code [here](../guide/example/example-poly-08.kt).
295
296An object serializes as an empty class, also using its fully-qualified class name as type by default:
297
298```text
299[{"type":"example.examplePoly08.EmptyResponse"},{"type":"example.examplePoly08.TextResponse","text":"OK"}]
300```
301
302<!--- TEST -->
303
304> Even if object has properties, they are not serialized.
305
306## Open polymorphism
307
308Serialization can work with arbitrary `open` classes or `abstract` classes.
309However, since this kind of polymorphism is open, there is a possibility that subclasses are defined anywhere in the
310source code, even in other modules, the list of subclasses that are serialized cannot be determined at compile-time and
311must be explicitly registered at runtime.
312
313### Registered subclasses
314
315Let us start with the code from the [Designing serializable hierarchy](#designing-serializable-hierarchy) section.
316To make it work with serialization without making it `sealed`, we have to define a [SerializersModule] using the
317[SerializersModule {}][SerializersModule()] builder function. In the module the base class is specified
318in the [polymorphic][_polymorphic] builder and each subclass is registered with the [subclass] function. Now,
319a custom JSON configuration can be instantiated with this module and used for serialization.
320
321> Details on custom JSON configurations can be found in
322> the [JSON configuration](json.md#json-configuration) section.
323
324<!--- INCLUDE
325import kotlinx.serialization.modules.*
326-->
327
328```kotlin
329val module = SerializersModule {
330    polymorphic(Project::class) {
331        subclass(OwnedProject::class)
332    }
333}
334
335val format = Json { serializersModule = module }
336
337@Serializable
338abstract class Project {
339    abstract val name: String
340}
341
342@Serializable
343@SerialName("owned")
344class OwnedProject(override val name: String, val owner: String) : Project()
345
346fun main() {
347    val data: Project = OwnedProject("kotlinx.coroutines", "kotlin")
348    println(format.encodeToString(data))
349}
350```
351
352> You can get the full code [here](../guide/example/example-poly-09.kt).
353
354This additional configuration makes our code work just as it worked with a sealed class in
355the [Sealed classes](#sealed-classes) section, but here subclasses can be spread arbitrarily throughout the code.
356
357```text
358{"type":"owned","name":"kotlinx.coroutines","owner":"kotlin"}
359```
360
361<!--- TEST -->
362>Please note that this example works only on JVM because of `serializer` function restrictions.
363>For JS and Native, explicit serializer should be used: `format.encodeToString(PolymorphicSerializer(Project::class), data)`
364>You can keep track of this issue [here](https://github.com/Kotlin/kotlinx.serialization/issues/1077).
365
366### Serializing interfaces
367
368We can update the previous example and turn `Project` superclass into an interface. However, we cannot
369mark an interface itself as `@Serializable`. No problem. Interfaces cannot have instances by themselves.
370Interfaces can only be represented by instances of their derived classes. Interfaces are used in the Kotlin language to enable polymorphism,
371so all interfaces are considered to be implicitly serializable with the [PolymorphicSerializer]
372strategy. We just need to mark their implementing classes as `@Serializable` and register them.
373
374<!--- INCLUDE
375import kotlinx.serialization.modules.*
376
377val module = SerializersModule {
378    polymorphic(Project::class) {
379        subclass(OwnedProject::class)
380    }
381}
382
383val format = Json { serializersModule = module }
384-->
385
386```kotlin
387interface Project {
388    val name: String
389}
390
391@Serializable
392@SerialName("owned")
393class OwnedProject(override val name: String, val owner: String) : Project
394```
395
396Now if we declare `data` with the type of `Project` we can simply call `format.encodeToString` as before.
397
398```kotlin
399fun main() {
400    val data: Project = OwnedProject("kotlinx.coroutines", "kotlin")
401    println(format.encodeToString(data))
402}
403```
404
405> You can get the full code [here](../guide/example/example-poly-10.kt).
406
407```text
408{"type":"owned","name":"kotlinx.coroutines","owner":"kotlin"}
409```
410
411> Note: On Kotlin/Native, you should use `format.encodeToString(PolymorphicSerializer(Project::class), data))` instead due to limited reflection capabilities.
412
413<!--- TEST LINES_START -->
414
415### Property of an interface type
416
417Continuing the previous example, let us see what happens if we use `Project` interface as a property in some
418other serializable class. Interfaces are implicitly polymorphic, so we can just declare a property of an interface type.
419
420<!--- INCLUDE
421import kotlinx.serialization.modules.*
422
423val module = SerializersModule {
424    polymorphic(Project::class) {
425        subclass(OwnedProject::class)
426    }
427}
428
429val format = Json { serializersModule = module }
430
431interface Project {
432    val name: String
433}
434
435@Serializable
436@SerialName("owned")
437class OwnedProject(override val name: String, val owner: String) : Project
438-->
439
440```kotlin
441@Serializable
442class Data(val project: Project) // Project is an interface
443
444fun main() {
445    val data = Data(OwnedProject("kotlinx.coroutines", "kotlin"))
446    println(format.encodeToString(data))
447}
448```
449
450> You can get the full code [here](../guide/example/example-poly-11.kt).
451
452As long as we've registered the actual subtype of the interface that is being serialized in
453the [SerializersModule] of our `format`, we get it working at runtime.
454
455```text
456{"project":{"type":"owned","name":"kotlinx.coroutines","owner":"kotlin"}}
457```
458
459<!--- TEST -->
460
461### Static parent type lookup for polymorphism
462
463During serialization of a polymorphic class the root type of the polymorphic hierarchy (`Project` in our example)
464is determined statically. Let us take the example with the serializable `abstract class Project`,
465but change the `main` function to declare `data` as having a type of `Any`:
466
467<!--- INCLUDE
468import kotlinx.serialization.modules.*
469
470val module = SerializersModule {
471    polymorphic(Project::class) {
472        subclass(OwnedProject::class)
473    }
474}
475
476val format = Json { serializersModule = module }
477
478@Serializable
479abstract class Project {
480    abstract val name: String
481}
482
483@Serializable
484@SerialName("owned")
485class OwnedProject(override val name: String, val owner: String) : Project()
486-->
487
488```kotlin
489fun main() {
490    val data: Any = OwnedProject("kotlinx.coroutines", "kotlin")
491    println(format.encodeToString(data))
492}
493```
494
495> You can get the full code [here](../guide/example/example-poly-12.kt).
496
497We get the exception.
498
499```text
500Exception in thread "main" kotlinx.serialization.SerializationException: Serializer for class 'Any' is not found.
501Please ensure that class is marked as '@Serializable' and that the serialization compiler plugin is applied.
502```
503
504<!--- TEST LINES_START -->
505
506We have to register classes for polymorphic serialization with respect for the corresponding static type we
507use in the source code. First of all, we change our module to register a subclass of `Any`:
508
509<!--- INCLUDE
510import kotlinx.serialization.modules.*
511-->
512
513```kotlin
514val module = SerializersModule {
515    polymorphic(Any::class) {
516        subclass(OwnedProject::class)
517    }
518}
519```
520
521<!--- INCLUDE
522val format = Json { serializersModule = module }
523
524@Serializable
525abstract class Project {
526    abstract val name: String
527}
528
529@Serializable
530@SerialName("owned")
531class OwnedProject(override val name: String, val owner: String) : Project()
532-->
533
534Then we can try to serialize the variable of type `Any`:
535
536```kotlin
537fun main() {
538    val data: Any = OwnedProject("kotlinx.coroutines", "kotlin")
539    println(format.encodeToString(data))
540}
541```
542
543> You can get the full code [here](../guide/example/example-poly-13.kt).
544
545However, `Any` is a class and it is not serializable:
546
547```text
548Exception in thread "main" kotlinx.serialization.SerializationException: Serializer for class 'Any' is not found.
549Please ensure that class is marked as '@Serializable' and that the serialization compiler plugin is applied.
550```
551
552<!--- TEST LINES_START -->
553
554We must to explicitly pass an instance of [PolymorphicSerializer] for the base class `Any` as the
555first parameter to the [encodeToString][Json.encodeToString] function.
556
557<!--- INCLUDE
558import kotlinx.serialization.modules.*
559
560val module = SerializersModule {
561    polymorphic(Any::class) {
562        subclass(OwnedProject::class)
563    }
564}
565
566val format = Json { serializersModule = module }
567
568@Serializable
569abstract class Project {
570    abstract val name: String
571}
572
573@Serializable
574@SerialName("owned")
575class OwnedProject(override val name: String, val owner: String) : Project()
576-->
577
578```kotlin
579fun main() {
580    val data: Any = OwnedProject("kotlinx.coroutines", "kotlin")
581    println(format.encodeToString(PolymorphicSerializer(Any::class), data))
582}
583```
584
585> You can get the full code [here](../guide/example/example-poly-14.kt).
586
587With the explicit serializer it works as before.
588
589```text
590{"type":"owned","name":"kotlinx.coroutines","owner":"kotlin"}
591```
592
593<!--- TEST -->
594
595### Explicitly marking polymorphic class properties
596
597The property of an interface type is implicitly considered polymorphic, since interfaces are all about runtime polymorphism.
598However, Kotlin Serialization does not compile a serializable class with a property of a non-serializable class type.
599If we have a property of `Any` class or other non-serializable class, then we must explicitly provide its serialization
600strategy via the [`@Serializable`][Serializable] annotation as we saw in
601the [Specifying serializer on a property](serializers.md#specifying-serializer-on-a-property) section.
602To specify a polymorphic serialization strategy of a property, the special-purpose [`@Polymorphic`][Polymorphic]
603annotation is used.
604
605<!--- INCLUDE
606import kotlinx.serialization.modules.*
607
608val module = SerializersModule {
609    polymorphic(Any::class) {
610        subclass(OwnedProject::class)
611    }
612}
613
614val format = Json { serializersModule = module }
615
616interface Project {
617    val name: String
618}
619
620@Serializable
621@SerialName("owned")
622class OwnedProject(override val name: String, val owner: String) : Project
623-->
624
625```kotlin
626@Serializable
627class Data(
628    @Polymorphic // the code does not compile without it
629    val project: Any
630)
631
632fun main() {
633    val data = Data(OwnedProject("kotlinx.coroutines", "kotlin"))
634    println(format.encodeToString(data))
635}
636```
637
638> You can get the full code [here](../guide/example/example-poly-15.kt).
639
640<!--- TEST
641{"project":{"type":"owned","name":"kotlinx.coroutines","owner":"kotlin"}}
642-->
643
644### Registering multiple superclasses
645
646When the same class gets serialized as a value of properties with different compile-time type from the list of
647its superclasses, we must register it in the [SerializersModule] for each of its superclasses separately.
648It is convenient to extract registration of all the subclasses into a separate function and
649use it for each superclass. You can use the following template to write it.
650
651<!--- INCLUDE
652import kotlinx.serialization.modules.*
653import kotlin.reflect.KClass
654-->
655
656```kotlin
657val module = SerializersModule {
658    fun PolymorphicModuleBuilder<Project>.registerProjectSubclasses() {
659        subclass(OwnedProject::class)
660    }
661    polymorphic(Any::class) { registerProjectSubclasses() }
662    polymorphic(Project::class) { registerProjectSubclasses() }
663}
664```
665
666<!--- INCLUDE
667
668val format = Json { serializersModule = module }
669
670interface Project {
671    val name: String
672}
673
674@Serializable
675@SerialName("owned")
676class OwnedProject(override val name: String, val owner: String) : Project
677
678@Serializable
679class Data(
680    val project: Project,
681    @Polymorphic val any: Any
682)
683
684fun main() {
685    val project = OwnedProject("kotlinx.coroutines", "kotlin")
686    val data = Data(project, project)
687    println(format.encodeToString(data))
688}
689-->
690
691> You can get the full code [here](../guide/example/example-poly-16.kt).
692
693<!--- TEST
694{"project":{"type":"owned","name":"kotlinx.coroutines","owner":"kotlin"},"any":{"type":"owned","name":"kotlinx.coroutines","owner":"kotlin"}}
695-->
696
697### Polymorphism and generic classes
698
699Generic subtypes for a serializable class require a special handling. Consider the following hierarchy.
700
701<!--- INCLUDE
702import kotlinx.serialization.modules.*
703-->
704
705```kotlin
706@Serializable
707abstract class Response<out T>
708
709@Serializable
710@SerialName("OkResponse")
711data class OkResponse<out T>(val data: T) : Response<T>()
712```
713
714Kotlin Serialization does not have a builtin strategy to represent the actually provided argument type for the
715type parameter `T` when serializing a property of the polymorphic type `OkResponse<T>`. We have to provide this
716strategy explicitly when defining the serializers module for the `Response`. In the below example we
717use `OkResponse.serializer(...)` to retrieve
718the [Plugin-generated generic serializer](serializers.md#plugin-generated-generic-serializer) of
719the `OkResponse` class and instantiate it with the [PolymorphicSerializer] instance with
720`Any` class as its base. This way, we can serialize an instance of `OkResponse` with any `data` property that
721was polymorphically registered as a subtype of `Any`.
722
723```kotlin
724val responseModule = SerializersModule {
725    polymorphic(Response::class) {
726        subclass(OkResponse.serializer(PolymorphicSerializer(Any::class)))
727    }
728}
729```
730
731### Merging library serializers modules
732
733When the application grows in size and splits into source code modules,
734it may become inconvenient to store all class hierarchies in one serializers module.
735Let us add a library with the `Project` hierarchy to the code from the previous section.
736
737```kotlin
738val projectModule = SerializersModule {
739    fun PolymorphicModuleBuilder<Project>.registerProjectSubclasses() {
740        subclass(OwnedProject::class)
741    }
742    polymorphic(Any::class) { registerProjectSubclasses() }
743    polymorphic(Project::class) { registerProjectSubclasses() }
744}
745```
746
747<!--- INCLUDE
748
749@Serializable
750abstract class Project {
751    abstract val name: String
752}
753
754@Serializable
755@SerialName("OwnedProject")
756data class OwnedProject(override val name: String, val owner: String) : Project()
757-->
758
759We can compose those two modules together using the  [plus] operator to merge them,
760so that we can use them both in the same [Json] format instance.
761
762> You can also use the [include][SerializersModuleBuilder.include] function
763> in the [SerializersModule {}][SerializersModule()] DSL.
764
765```kotlin
766val format = Json { serializersModule = projectModule + responseModule }
767````
768
769Now classes from both hierarchies can be serialized together and deserialized together.
770
771```kotlin
772fun main() {
773    // both Response and Project are abstract and their concrete subtypes are being serialized
774    val data: Response<Project> =  OkResponse(OwnedProject("kotlinx.serialization", "kotlin"))
775    val string = format.encodeToString(data)
776    println(string)
777    println(format.decodeFromString<Response<Project>>(string))
778}
779
780```
781
782> You can get the full code [here](../guide/example/example-poly-17.kt).
783
784The JSON that is being produced is deeply polymorphic.
785
786```text
787{"type":"OkResponse","data":{"type":"OwnedProject","name":"kotlinx.serialization","owner":"kotlin"}}
788OkResponse(data=OwnedProject(name=kotlinx.serialization, owner=kotlin))
789```
790
791<!--- TEST -->
792
793If you're writing a library or shared module with an abstract class and some implementations of it,
794you can expose your own serializers module for your clients to use so that a client can combine your
795module with their modules.
796
797### Default polymorphic type handler for deserialization
798
799What happens when we deserialize a subclass that was not registered?
800
801<!--- INCLUDE
802import kotlinx.serialization.modules.*
803
804@Serializable
805abstract class Project {
806    abstract val name: String
807}
808
809@Serializable
810@SerialName("OwnedProject")
811data class OwnedProject(override val name: String, val owner: String) : Project()
812
813val module = SerializersModule {
814    polymorphic(Project::class) {
815        subclass(OwnedProject::class)
816    }
817}
818
819val format = Json { serializersModule = module }
820-->
821
822```kotlin
823fun main() {
824    println(format.decodeFromString<Project>("""
825        {"type":"unknown","name":"example"}
826    """))
827}
828```
829
830> You can get the full code [here](../guide/example/example-poly-18.kt).
831
832We get the following exception.
833
834```text
835Exception in thread "main" kotlinx.serialization.json.internal.JsonDecodingException: Unexpected JSON token at offset 0: Serializer for subclass 'unknown' is not found in the polymorphic scope of 'Project' at path: $
836Check if class with serial name 'unknown' exists and serializer is registered in a corresponding SerializersModule.
837```
838
839<!--- TEST LINES_START -->
840
841When reading a flexible input we might want to provide some default behavior in this case. For example,
842we can have a `BasicProject` subtype to represent all kinds of unknown `Project` subtypes.
843
844<!--- INCLUDE
845import kotlinx.serialization.modules.*
846-->
847
848```kotlin
849@Serializable
850abstract class Project {
851    abstract val name: String
852}
853
854@Serializable
855data class BasicProject(override val name: String, val type: String): Project()
856
857@Serializable
858@SerialName("OwnedProject")
859data class OwnedProject(override val name: String, val owner: String) : Project()
860```
861
862We register a default deserializer handler using the [`defaultDeserializer`][PolymorphicModuleBuilder.defaultDeserializer] function in
863the [`polymorphic { ... }`][PolymorphicModuleBuilder] DSL that defines a strategy which maps the `type` string from the input
864to the [deserialization strategy][DeserializationStrategy]. In the below example we don't use the type,
865but always return the [Plugin-generated serializer](serializers.md#plugin-generated-serializer)
866of the `BasicProject` class.
867
868```kotlin
869val module = SerializersModule {
870    polymorphic(Project::class) {
871        subclass(OwnedProject::class)
872        defaultDeserializer { BasicProject.serializer() }
873    }
874}
875```
876
877Using this module we can now deserialize both instances of the registered `OwnedProject` and
878any unregistered one.
879
880```kotlin
881val format = Json { serializersModule = module }
882
883fun main() {
884    println(format.decodeFromString<List<Project>>("""
885        [
886            {"type":"unknown","name":"example"},
887            {"type":"OwnedProject","name":"kotlinx.serialization","owner":"kotlin"}
888        ]
889    """))
890}
891```
892
893> You can get the full code [here](../guide/example/example-poly-19.kt).
894
895Notice, how `BasicProject` had also captured the specified type key in its `type` property.
896
897```text
898[BasicProject(name=example, type=unknown), OwnedProject(name=kotlinx.serialization, owner=kotlin)]
899```
900
901<!--- TEST -->
902
903We used a plugin-generated serializer as a default serializer, implying that
904the structure of the "unknown" data is known in advance. In a real-world API it's rarely the case.
905For that purpose a custom, less-structured serializer is needed. You will see the example of such serializer in the future section
906on [Maintaining custom JSON attributes](json.md#maintaining-custom-json-attributes).
907
908### Default polymorphic type handler for serialization
909
910Sometimes you need to dynamically choose which serializer to use for a polymorphic type based on the instance, for example if you
911don't have access to the full type hierarchy, or if it changes a lot. For this situation, you can register a default serializer.
912
913<!--- INCLUDE
914import kotlinx.serialization.descriptors.*
915import kotlinx.serialization.encoding.*
916import kotlinx.serialization.modules.*
917-->
918
919```kotlin
920interface Animal {
921}
922
923interface Cat : Animal {
924    val catType: String
925}
926
927interface Dog : Animal {
928    val dogType: String
929}
930
931private class CatImpl : Cat {
932    override val catType: String = "Tabby"
933}
934
935private class DogImpl : Dog {
936    override val dogType: String = "Husky"
937}
938
939object AnimalProvider {
940    fun createCat(): Cat = CatImpl()
941    fun createDog(): Dog = DogImpl()
942}
943```
944
945We register a default serializer handler using the [`polymorphicDefaultSerializer`][SerializersModuleBuilder.polymorphicDefaultSerializer] function in
946the [`SerializersModule { ... }`][SerializersModuleBuilder] DSL that defines a strategy which takes an instance of the base class and
947provides a [serialization strategy][SerializationStrategy]. In the below example we use a `when` block to check the type of the
948instance, without ever having to refer to the private implementation classes.
949
950```kotlin
951val module = SerializersModule {
952    polymorphicDefaultSerializer(Animal::class) { instance ->
953        @Suppress("UNCHECKED_CAST")
954        when (instance) {
955            is Cat -> CatSerializer as SerializationStrategy<Animal>
956            is Dog -> DogSerializer as SerializationStrategy<Animal>
957            else -> null
958        }
959    }
960}
961
962object CatSerializer : SerializationStrategy<Cat> {
963    override val descriptor = buildClassSerialDescriptor("Cat") {
964        element<String>("catType")
965    }
966
967    override fun serialize(encoder: Encoder, value: Cat) {
968        encoder.encodeStructure(descriptor) {
969          encodeStringElement(descriptor, 0, value.catType)
970        }
971    }
972}
973
974object DogSerializer : SerializationStrategy<Dog> {
975  override val descriptor = buildClassSerialDescriptor("Dog") {
976    element<String>("dogType")
977  }
978
979  override fun serialize(encoder: Encoder, value: Dog) {
980    encoder.encodeStructure(descriptor) {
981      encodeStringElement(descriptor, 0, value.dogType)
982    }
983  }
984}
985```
986
987Using this module we can now serialize instances of `Cat` and `Dog`.
988
989```kotlin
990val format = Json { serializersModule = module }
991
992fun main() {
993    println(format.encodeToString<Animal>(AnimalProvider.createCat()))
994}
995```
996
997> You can get the full code [here](../guide/example/example-poly-20.kt)
998
999```text
1000{"type":"Cat","catType":"Tabby"}
1001```
1002
1003
1004<!--- TEST -->
1005
1006---
1007
1008The next chapter covers [JSON features](json.md).
1009
1010<!--- MODULE /kotlinx-serialization-core -->
1011<!--- INDEX kotlinx-serialization-core/kotlinx.serialization -->
1012
1013[SerialName]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serial-name/index.html
1014[PolymorphicSerializer]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-polymorphic-serializer/index.html
1015[Serializable]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serializable/index.html
1016[Polymorphic]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-polymorphic/index.html
1017[DeserializationStrategy]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-deserialization-strategy/index.html
1018[SerializationStrategy]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization/-serialization-strategy/index.html
1019
1020<!--- INDEX kotlinx-serialization-core/kotlinx.serialization.modules -->
1021
1022[SerializersModule]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.modules/-serializers-module/index.html
1023[SerializersModule()]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.modules/-serializers-module.html
1024[_polymorphic]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.modules/polymorphic.html
1025[subclass]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.modules/subclass.html
1026[plus]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.modules/plus.html
1027[SerializersModuleBuilder.include]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.modules/-serializers-module-builder/include.html
1028[PolymorphicModuleBuilder.defaultDeserializer]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.modules/-polymorphic-module-builder/default-deserializer.html
1029[PolymorphicModuleBuilder]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.modules/-polymorphic-module-builder/index.html
1030[SerializersModuleBuilder.polymorphicDefaultSerializer]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.modules/-serializers-module-builder/polymorphic-default-serializer.html
1031[SerializersModuleBuilder]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-core/kotlinx.serialization.modules/-serializers-module-builder/index.html
1032
1033<!--- MODULE /kotlinx-serialization-json -->
1034<!--- INDEX kotlinx-serialization-json/kotlinx.serialization.json -->
1035
1036[Json.encodeToString]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/encode-to-string.html
1037[Json]: https://kotlinlang.org/api/kotlinx.serialization/kotlinx-serialization-json/kotlinx.serialization.json/-json/index.html
1038
1039<!--- END -->
1040
1041