• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3  */
4 
5 package kotlinx.serialization.json.internal
6 
7 import kotlinx.serialization.*
8 import kotlinx.serialization.descriptors.*
9 import kotlinx.serialization.modules.*
10 import kotlin.reflect.*
11 
12 @OptIn(ExperimentalSerializationApi::class)
13 internal class PolymorphismValidator(
14     private val useArrayPolymorphism: Boolean,
15     private val discriminator: String
16 ) : SerializersModuleCollector {
17 
contextualnull18     override fun <T : Any> contextual(
19         kClass: KClass<T>,
20         provider: (typeArgumentsSerializers: List<KSerializer<*>>) -> KSerializer<*>
21     ) {
22         // Nothing here
23     }
24 
polymorphicnull25     override fun <Base : Any, Sub : Base> polymorphic(
26         baseClass: KClass<Base>,
27         actualClass: KClass<Sub>,
28         actualSerializer: KSerializer<Sub>
29     ) {
30         val descriptor = actualSerializer.descriptor
31         checkKind(descriptor, actualClass)
32         if (!useArrayPolymorphism) {
33             // Collisions with "type" can happen only for JSON polymorphism
34             checkDiscriminatorCollisions(descriptor, actualClass)
35         }
36     }
37 
checkKindnull38     private fun checkKind(descriptor: SerialDescriptor, actualClass: KClass<*>) {
39         val kind = descriptor.kind
40         if (kind is PolymorphicKind || kind == SerialKind.CONTEXTUAL) {
41             throw IllegalArgumentException("Serializer for ${actualClass.simpleName} can't be registered as a subclass for polymorphic serialization " +
42                     "because its kind $kind is not concrete. To work with multiple hierarchies, register it as a base class.")
43         }
44 
45         if (useArrayPolymorphism) return
46         /*
47          * For this kind we can't intercept the JSON object {} in order to add "type: ...".
48          * Except for maps that just can clash and accidentally overwrite the type.
49          */
50         if (kind == StructureKind.LIST || kind == StructureKind.MAP
51             || kind is PrimitiveKind
52             || kind is SerialKind.ENUM
53         ) {
54             throw IllegalArgumentException(
55                 "Serializer for ${actualClass.simpleName} of kind $kind cannot be serialized polymorphically with class discriminator."
56             )
57         }
58     }
59 
checkDiscriminatorCollisionsnull60     private fun checkDiscriminatorCollisions(
61         descriptor: SerialDescriptor,
62         actualClass: KClass<*>
63     ) {
64         for (i in 0 until descriptor.elementsCount) {
65             val name = descriptor.getElementName(i)
66             if (name == discriminator) {
67                 throw IllegalArgumentException(
68                     "Polymorphic serializer for $actualClass has property '$name' that conflicts " +
69                             "with JSON class discriminator. You can either change class discriminator in JsonConfiguration, " +
70                             "rename property with @SerialName annotation " +
71                             "or fall back to array polymorphism"
72                 )
73             }
74         }
75     }
76 
polymorphicDefaultSerializernull77     override fun <Base : Any> polymorphicDefaultSerializer(
78         baseClass: KClass<Base>,
79         defaultSerializerProvider: (value: Base) -> SerializationStrategy<Base>?
80     ) {
81         // Nothing here
82     }
83 
polymorphicDefaultDeserializernull84     override fun <Base : Any> polymorphicDefaultDeserializer(
85         baseClass: KClass<Base>,
86         defaultDeserializerProvider: (className: String?) -> DeserializationStrategy<Base>?
87     ) {
88         // Nothing here
89     }
90 }
91