All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.kotlinx.serialization.modules.SerializersModule.kt Maven / Gradle / Ivy

/*
 * 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.js.*
import kotlin.jvm.*
import kotlin.reflect.*

/**
 * [SerializersModule] is a collection of serializers used by [ContextualSerializer] and [PolymorphicSerializer]
 * to override or provide serializers at the runtime, whereas at the compile-time they provided by the serialization plugin.
 * It can be considered as a map where serializers can be found using their statically known KClasses.
 *
 * To enable runtime serializers resolution, one of the special annotations must be used on target types
 * ([Polymorphic] or [Contextual]), and a serial module with serializers should be used during construction of [SerialFormat].
 *
 * Serializers module can be built with `SerializersModule {}` builder function.
 * Empty module can be obtained with `EmptySerializersModule()` factory function.
 *
 * @see Contextual
 * @see Polymorphic
 */
public sealed class SerializersModule {

    @ExperimentalSerializationApi
    @Deprecated(
        "Deprecated in favor of overload with default parameter",
        ReplaceWith("getContextual(kclass)"),
        DeprecationLevel.HIDDEN
    ) // Was experimental since 1.0.0, HIDDEN in 1.2.0 in a backwards-compatible manner
    public fun  getContextual(kclass: KClass): KSerializer? =
        getContextual(kclass, emptyList())

    /**
     * Returns a contextual serializer associated with a given [kClass].
     * If given class has generic parameters and module has provider for [kClass],
     * [typeArgumentsSerializers] are used to create serializer.
     * This method is used in context-sensitive operations on a property marked with [Contextual] by a [ContextualSerializer].
     *
     * @see SerializersModuleBuilder.contextual
     */
    @ExperimentalSerializationApi
    public abstract fun  getContextual(
        kClass: KClass,
        typeArgumentsSerializers: List> = emptyList()
    ): KSerializer?

    /**
     * Returns a polymorphic serializer registered for a class of the given [value] in the scope of [baseClass].
     */
    @ExperimentalSerializationApi
    public abstract fun  getPolymorphic(baseClass: KClass, value: T): SerializationStrategy?

    /**
     * Returns a polymorphic deserializer registered for a [serializedClassName] in the scope of [baseClass]
     * or default value constructed from [serializedClassName] if a default serializer provider was registered.
     */
    @ExperimentalSerializationApi
    public abstract fun  getPolymorphic(baseClass: KClass, serializedClassName: String?): DeserializationStrategy?

    /**
     * Copies contents of this module to the given [collector].
     */
    @ExperimentalSerializationApi
    public abstract fun dumpTo(collector: SerializersModuleCollector)
}

/**
 * A [SerializersModule] which is empty and always returns `null`.
 */
@Deprecated("Deprecated in the favour of 'EmptySerializersModule()'",
    level = DeprecationLevel.WARNING,
    replaceWith = ReplaceWith("EmptySerializersModule()"))
@JsName("EmptySerializersModuleLegacyJs") // Compatibility with JS
public val EmptySerializersModule: SerializersModule = SerialModuleImpl(emptyMap(), emptyMap(), emptyMap(), emptyMap(), emptyMap())

/**
 * Returns a combination of two serial modules
 *
 * If serializer for some class presents in both modules, a [SerializerAlreadyRegisteredException] is thrown.
 * To overwrite serializers, use [SerializersModule.overwriteWith] function.
 */
public operator fun SerializersModule.plus(other: SerializersModule): SerializersModule = SerializersModule {
    include(this@plus)
    include(other)
}

/**
 * Returns a combination of two serial modules
 *
 * If serializer for some class presents in both modules, result module
 * will contain serializer from [other] module.
 */
@OptIn(ExperimentalSerializationApi::class)
public infix fun SerializersModule.overwriteWith(other: SerializersModule): SerializersModule = SerializersModule {
    include(this@overwriteWith)
    other.dumpTo(object : SerializersModuleCollector {
        override fun  contextual(kClass: KClass, serializer: KSerializer) {
            registerSerializer(kClass, ContextualProvider.Argless(serializer), allowOverwrite = true)
        }

        override fun  contextual(
            kClass: KClass,
            provider: (serializers: List>) -> KSerializer<*>
        ) {
            registerSerializer(kClass, ContextualProvider.WithTypeArguments(provider), allowOverwrite = true)
        }

        override fun  polymorphic(
            baseClass: KClass,
            actualClass: KClass,
            actualSerializer: KSerializer
        ) {
            registerPolymorphicSerializer(baseClass, actualClass, actualSerializer, allowOverwrite = true)
        }

        override fun  polymorphicDefaultSerializer(
            baseClass: KClass,
            defaultSerializerProvider: (value: Base) -> SerializationStrategy?
        ) {
            registerDefaultPolymorphicSerializer(baseClass, defaultSerializerProvider, allowOverwrite = true)
        }

        override fun  polymorphicDefaultDeserializer(
            baseClass: KClass,
            defaultDeserializerProvider: (className: String?) -> DeserializationStrategy?
        ) {
            registerDefaultPolymorphicDeserializer(baseClass, defaultDeserializerProvider, allowOverwrite = true)
        }
    })
}

// Implementation details below

/**
 * A default implementation of [SerializersModule]
 * which uses hash maps to store serializers associated with KClasses.
 */
@Suppress("UNCHECKED_CAST")
@OptIn(ExperimentalSerializationApi::class)
internal class SerialModuleImpl(
    private val class2ContextualFactory: Map, ContextualProvider>,
    @JvmField val polyBase2Serializers: Map, Map, KSerializer<*>>>,
    private val polyBase2DefaultSerializerProvider: Map, PolymorphicSerializerProvider<*>>,
    private val polyBase2NamedSerializers: Map, Map>>,
    private val polyBase2DefaultDeserializerProvider: Map, PolymorphicDeserializerProvider<*>>
) : SerializersModule() {

    override fun  getPolymorphic(baseClass: KClass, value: T): SerializationStrategy? {
        if (!baseClass.isInstance(value)) return null
        // Registered
        val registered = polyBase2Serializers[baseClass]?.get(value::class) as? SerializationStrategy
        if (registered != null) return registered
        // Default
        return (polyBase2DefaultSerializerProvider[baseClass] as? PolymorphicSerializerProvider)?.invoke(value)
    }

    override fun  getPolymorphic(baseClass: KClass, serializedClassName: String?): DeserializationStrategy? {
        // Registered
        val registered = polyBase2NamedSerializers[baseClass]?.get(serializedClassName) as? KSerializer
        if (registered != null) return registered
        // Default
        return (polyBase2DefaultDeserializerProvider[baseClass] as? PolymorphicDeserializerProvider)?.invoke(serializedClassName)
    }

    override fun  getContextual(kClass: KClass, typeArgumentsSerializers: List>): KSerializer? {
        return (class2ContextualFactory[kClass]?.invoke(typeArgumentsSerializers)) as? KSerializer?
    }

    override fun dumpTo(collector: SerializersModuleCollector) {
        class2ContextualFactory.forEach { (kclass, serial) ->
            when (serial) {
                is ContextualProvider.Argless -> collector.contextual(
                    kclass as KClass,
                    serial.serializer as KSerializer
                )
                is ContextualProvider.WithTypeArguments -> collector.contextual(kclass, serial.provider)
            }
        }

        polyBase2Serializers.forEach { (baseClass, classMap) ->
            classMap.forEach { (actualClass, serializer) ->
                collector.polymorphic(
                    baseClass as KClass,
                    actualClass as KClass,
                    serializer.cast()
                )
            }
        }

        polyBase2DefaultSerializerProvider.forEach { (baseClass, provider) ->
            collector.polymorphicDefaultSerializer(baseClass as KClass, provider as (PolymorphicSerializerProvider))
        }

        polyBase2DefaultDeserializerProvider.forEach { (baseClass, provider) ->
            collector.polymorphicDefaultDeserializer(baseClass as KClass, provider as (PolymorphicDeserializerProvider))
        }
    }
}

internal typealias PolymorphicDeserializerProvider = (className: String?) -> DeserializationStrategy?
internal typealias PolymorphicSerializerProvider = (value: Base) -> SerializationStrategy?

/** This class is needed to support re-registering the same static (argless) serializers:
 *
 * ```
 * val m1 = serializersModuleOf(A::class, A.serializer())
 * val m2 = serializersModuleOf(A::class, A.serializer())
 * val aggregate = m1 + m2 // should not throw
 * ```
 */
internal sealed class ContextualProvider {
    abstract operator fun invoke(typeArgumentsSerializers: List>): KSerializer<*>

    class Argless(val serializer: KSerializer<*>) : ContextualProvider() {
        override fun invoke(typeArgumentsSerializers: List>): KSerializer<*> = serializer

        override fun equals(other: Any?): Boolean = other is Argless && other.serializer == this.serializer

        override fun hashCode(): Int = serializer.hashCode()
    }

    class WithTypeArguments(val provider: (typeArgumentsSerializers: List>) -> KSerializer<*>) :
        ContextualProvider() {
        override fun invoke(typeArgumentsSerializers: List>): KSerializer<*> =
            provider(typeArgumentsSerializers)
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy