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