/* * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. */ package kotlinx.serialization.modules import kotlinx.serialization.* import kotlinx.serialization.internal.* import kotlin.jvm.* import kotlin.reflect.* /** * Returns a [SerializersModule] which has one class with one [serializer] for [ContextualSerializer]. */ public fun serializersModuleOf(kClass: KClass, serializer: KSerializer): SerializersModule = SerializersModule { contextual(kClass, serializer) } /** * Returns a [SerializersModule] which has one class with one [serializer] for [ContextualSerializer]. */ public inline fun serializersModuleOf(serializer: KSerializer): SerializersModule = serializersModuleOf(T::class, serializer) /** * A builder function for creating a [SerializersModule]. * Serializers can be added via [SerializersModuleBuilder.contextual] or [SerializersModuleBuilder.polymorphic]. * Since [SerializersModuleBuilder] also implements [SerialModuleCollector], * it is possible to copy whole another module to this builder with [SerializersModule.dumpTo] */ @Suppress("FunctionName") public inline fun SerializersModule(builderAction: SerializersModuleBuilder.() -> Unit): SerializersModule { val builder = SerializersModuleBuilder() builder.builderAction() return builder.build() } /** * A [SerializersModule] which is empty and returns `null` from each method. */ @Suppress("FunctionName") public fun EmptySerializersModule(): SerializersModule = @Suppress("DEPRECATION") EmptySerializersModule /** * A builder class for [SerializersModule] DSL. To create an instance of builder, use [SerializersModule] factory function. */ @OptIn(ExperimentalSerializationApi::class) public class SerializersModuleBuilder @PublishedApi internal constructor() : SerializersModuleCollector { private val class2ContextualProvider: MutableMap, ContextualProvider> = hashMapOf() private val polyBase2Serializers: MutableMap, MutableMap, KSerializer<*>>> = hashMapOf() private val polyBase2DefaultSerializerProvider: MutableMap, PolymorphicSerializerProvider<*>> = hashMapOf() private val polyBase2NamedSerializers: MutableMap, MutableMap>> = hashMapOf() private val polyBase2DefaultDeserializerProvider: MutableMap, PolymorphicDeserializerProvider<*>> = hashMapOf() /** * Adds [serializer] associated with given [kClass] for contextual serialization. * If [kClass] has generic type parameters, consider registering provider instead. * * Throws [SerializationException] if a module already has serializer or provider associated with a [kClass]. * To overwrite an already registered serializer, [SerializersModule.overwriteWith] can be used. */ public override fun contextual(kClass: KClass, serializer: KSerializer): Unit = registerSerializer(kClass, ContextualProvider.Argless(serializer)) /** * Registers [provider] associated with given generic [kClass] for contextual serialization. * When a serializer is requested from a module, provider is being called with type arguments serializers * of the particular [kClass] usage. * * Example: * ``` * class Holder(@Contextual val boxI: Box, @Contextual val boxS: Box) * * val module = SerializersModule { * // args[0] contains Int.serializer() or String.serializer(), depending on the property * contextual(Box::class) { args -> BoxSerializer(args[0]) } * } * ``` * * Throws [SerializationException] if a module already has provider or serializer associated with a [kClass]. * To overwrite an already registered serializer, [SerializersModule.overwriteWith] can be used. */ public override fun contextual( kClass: KClass, provider: (typeArgumentsSerializers: List>) -> KSerializer<*> ): Unit = registerSerializer(kClass, ContextualProvider.WithTypeArguments(provider)) /** * Adds [serializer][actualSerializer] associated with given [actualClass] in the scope of [baseClass] for polymorphic serialization. * Throws [SerializationException] if a module already has serializer associated with a [actualClass]. * To overwrite an already registered serializer, [SerializersModule.overwriteWith] can be used. */ public override fun polymorphic( baseClass: KClass, actualClass: KClass, actualSerializer: KSerializer ) { registerPolymorphicSerializer(baseClass, actualClass, actualSerializer) } /** * Adds a default serializers provider associated with the given [baseClass] to the resulting module. * [defaultSerializerProvider] is invoked when no polymorphic serializers for `value` in the scope of [baseClass] were found. * * Default serializers provider affects only serialization process. To affect deserialization process, use * [SerializersModuleBuilder.polymorphicDefaultDeserializer]. * * [defaultSerializerProvider] can be stateful and lookup a serializer for the missing type dynamically. */ public override fun polymorphicDefaultSerializer( baseClass: KClass, defaultSerializerProvider: (value: Base) -> SerializationStrategy? ) { registerDefaultPolymorphicSerializer(baseClass, defaultSerializerProvider, false) } /** * Adds a default deserializers provider associated with the given [baseClass] to the resulting module. * [defaultDeserializerProvider] is invoked when no polymorphic serializers associated with the `className` * in the scope of [baseClass] were found. `className` could be `null` for formats that support nullable class discriminators * (currently only `Json` with `useArrayPolymorphism` set to `false`). * * Default deserializers provider affects only deserialization process. To affect serialization process, use * [SerializersModuleBuilder.polymorphicDefaultSerializer]. * * [defaultDeserializerProvider] can be stateful and lookup a serializer for the missing type dynamically. * * @see PolymorphicModuleBuilder.defaultDeserializer */ public override fun polymorphicDefaultDeserializer( baseClass: KClass, defaultDeserializerProvider: (className: String?) -> DeserializationStrategy? ) { registerDefaultPolymorphicDeserializer(baseClass, defaultDeserializerProvider, false) } /** * Copies the content of [module] module into the current builder. */ public fun include(module: SerializersModule) { module.dumpTo(this) } @JvmName("registerSerializer") // Don't mangle method name for prettier stack traces internal fun registerSerializer( forClass: KClass, provider: ContextualProvider, allowOverwrite: Boolean = false ) { if (!allowOverwrite) { val previous = class2ContextualProvider[forClass] if (previous != null && previous != provider) { // How can we provide meaningful name for WithTypeArgumentsProvider ? throw SerializerAlreadyRegisteredException( "Contextual serializer or serializer provider for $forClass already registered in this module" ) } } class2ContextualProvider[forClass] = provider } @JvmName("registerDefaultPolymorphicSerializer") // Don't mangle method name for prettier stack traces internal fun registerDefaultPolymorphicSerializer( baseClass: KClass, defaultSerializerProvider: (value: Base) -> SerializationStrategy?, allowOverwrite: Boolean ) { val previous = polyBase2DefaultSerializerProvider[baseClass] if (previous != null && previous != defaultSerializerProvider && !allowOverwrite) { throw IllegalArgumentException("Default serializers provider for $baseClass is already registered: $previous") } polyBase2DefaultSerializerProvider[baseClass] = defaultSerializerProvider } @JvmName("registerDefaultPolymorphicDeserializer") // Don't mangle method name for prettier stack traces internal fun registerDefaultPolymorphicDeserializer( baseClass: KClass, defaultDeserializerProvider: (className: String?) -> DeserializationStrategy?, allowOverwrite: Boolean ) { val previous = polyBase2DefaultDeserializerProvider[baseClass] if (previous != null && previous != defaultDeserializerProvider && !allowOverwrite) { throw IllegalArgumentException("Default deserializers provider for $baseClass is already registered: $previous") } polyBase2DefaultDeserializerProvider[baseClass] = defaultDeserializerProvider } @JvmName("registerPolymorphicSerializer") // Don't mangle method name for prettier stack traces internal fun registerPolymorphicSerializer( baseClass: KClass, concreteClass: KClass, concreteSerializer: KSerializer, allowOverwrite: Boolean = false ) { // Check for overwrite val name = concreteSerializer.descriptor.serialName val baseClassSerializers = polyBase2Serializers.getOrPut(baseClass, ::hashMapOf) val previousSerializer = baseClassSerializers[concreteClass] val names = polyBase2NamedSerializers.getOrPut(baseClass, ::hashMapOf) if (allowOverwrite) { // Remove previous serializers from name mapping if (previousSerializer != null) { names.remove(previousSerializer.descriptor.serialName) } // Update mappings baseClassSerializers[concreteClass] = concreteSerializer names[name] = concreteSerializer return } // Overwrite prohibited if (previousSerializer != null) { if (previousSerializer != concreteSerializer) { throw SerializerAlreadyRegisteredException(baseClass, concreteClass) } else { // Cleanup name mapping names.remove(previousSerializer.descriptor.serialName) } } val previousByName = names[name] if (previousByName != null) { val conflictingClass = polyBase2Serializers[baseClass]!!.asSequence().find { it.value === previousByName } throw IllegalArgumentException( "Multiple polymorphic serializers for base class '$baseClass' " + "have the same serial name '$name': '$concreteClass' and '$conflictingClass'" ) } // Overwrite if no conflicts baseClassSerializers[concreteClass] = concreteSerializer names[name] = concreteSerializer } @PublishedApi internal fun build(): SerializersModule = SerialModuleImpl(class2ContextualProvider, polyBase2Serializers, polyBase2DefaultSerializerProvider, polyBase2NamedSerializers, polyBase2DefaultDeserializerProvider) } /** * Adds [serializer] associated with given type [T] for contextual serialization. * Throws [SerializationException] if a module already has serializer associated with the given type. * To overwrite an already registered serializer, [SerializersModule.overwriteWith] can be used. */ public inline fun SerializersModuleBuilder.contextual(serializer: KSerializer): Unit = contextual(T::class, serializer) /** * Creates a builder to register subclasses of a given [baseClass] for polymorphic serialization. * If [baseSerializer] is not null, registers it as a serializer for [baseClass], * which is useful if the base class is serializable itself. To register subclasses, * [PolymorphicModuleBuilder.subclass] builder function can be used. * * If a serializer already registered for the given KClass in the given scope, an [IllegalArgumentException] is thrown. * To override registered serializers, combine built module with another using [SerializersModule.overwriteWith]. * * @see PolymorphicSerializer */ public inline fun SerializersModuleBuilder.polymorphic( baseClass: KClass, baseSerializer: KSerializer? = null, builderAction: PolymorphicModuleBuilder.() -> Unit = {} ) { val builder = PolymorphicModuleBuilder(baseClass, baseSerializer) builder.builderAction() builder.buildTo(this) } private class SerializerAlreadyRegisteredException internal constructor(msg: String) : IllegalArgumentException(msg) { internal constructor( baseClass: KClass<*>, concreteClass: KClass<*> ) : this("Serializer for $concreteClass already registered in the scope of $baseClass") }