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-2020 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.native.concurrent.*
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].
 *
 * @see Contextual
 * @see Polymorphic
 */
public sealed class SerializersModule {

    /**
     * Returns a contextual serializer associated with a given [kclass].
     * This method is used in context-sensitive operations on a property marked with [Contextual] by a [ContextualSerializer]
     */
    public abstract fun  getContextual(kclass: KClass): KSerializer?

    /**
     * Returns a polymorphic serializer registered for a class of the given [value] in the scope of [baseClass].
     */
    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 default serializer provider was registered.
     */
    public abstract fun  getPolymorphic(baseClass: KClass, serializedClassName: String): DeserializationStrategy?

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

/**
 * A [SerializersModule] which is empty and always returns `null`.
 */
@SharedImmutable
public val EmptySerializersModule: SerializersModule = SerialModuleImpl(emptyMap(), emptyMap(), emptyMap(), emptyMap())

/**
 * Attempts to retrieve a serializer from the current module and, if not found, fallbacks to [serializer] method
 */
@OptIn(UnsafeSerializationApi::class)
public inline fun  SerializersModule.getContextualOrDefault(): KSerializer =
// Even though serializer(KType) also invokes serializerOrNull, it is a significant performance optimization
    // TODO replace with serializer(typeOf()) when intrinsics are here
    getContextual(T::class) ?: T::class.serializerOrNull() ?: serializer(typeOf()).cast()

/**
 * Attempts to retrieve a serializer from the current module using the given [type] and, if not found, fallbacks to [serializer] method
 */
@OptIn(UnsafeSerializationApi::class)
public fun  SerializersModule.getContextualOrDefault(type: KType): KSerializer {
    // Even though serializer(KType) also invokes serializerOrNull, it is a significant performance optimization
    // TODO replace with serializer(typeOf()) when intrinsics are here
    val kclass = type.kclass()
    return (getContextual(kclass) ?: kclass.serializerOrNull() ?: serializer(type)).cast()
}

/**
 * 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.
 */
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, serializer, allowOverwrite = true)
        }

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

        override fun  polymorphicDefault(
            baseClass: KClass,
            defaultSerializerProvider: (className: String) -> DeserializationStrategy?
        ) {
            registerDefaultPolymorphicSerializer(baseClass, defaultSerializerProvider, allowOverwrite = true)
        }
    })
}

// Implementation details below

/**
 * A default implementation of [SerializersModule]
 * which uses hash maps to store serializers associated with KClasses.
 */
@Suppress("UNCHECKED_CAST")
internal class SerialModuleImpl(
    private val class2Serializer: Map, KSerializer<*>>,
    @JvmField val polyBase2Serializers: Map, Map, KSerializer<*>>>,
    private val polyBase2NamedSerializers: Map, Map>>,
    private val polyBase2DefaultProvider: Map, PolymorphicProvider<*>>
) : SerializersModule() {

    override fun  getPolymorphic(baseClass: KClass, value: T): SerializationStrategy? {
        if (!value.isInstanceOf(baseClass)) return null
        val custom = polyBase2Serializers[baseClass]?.get(value::class) as? SerializationStrategy
        if (custom != null) return custom
        if (baseClass == Any::class) {
            val serializer = StandardSubtypesOfAny.getSubclassSerializer(value)
            return serializer as? SerializationStrategy
        }
        return null
    }

    override fun  getPolymorphic(baseClass: KClass, serializedClassName: String): DeserializationStrategy? {
        // Registered
        val registered = polyBase2NamedSerializers[baseClass]?.get(serializedClassName) as? KSerializer
        if (registered != null) return registered
        // Default
        val default = (polyBase2DefaultProvider[baseClass] as? PolymorphicProvider)?.invoke(serializedClassName)
        if (default != null) return default
        // Any subtypes
        return if (baseClass == Any::class)
            StandardSubtypesOfAny.getDefaultDeserializer(serializedClassName)?.cast()
        else null
    }

    override fun  getContextual(kclass: KClass): KSerializer? =
        class2Serializer[kclass] as? KSerializer

    override fun dumpTo(collector: SerializersModuleCollector) {
        class2Serializer.forEach { (kclass, serial) ->
            collector.contextual(
                kclass as KClass,
                serial.cast()
            )
        }

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

        polyBase2DefaultProvider.forEach { (baseClass, provider) ->
            collector.polymorphicDefault(baseClass as KClass, provider as (PolymorphicProvider))
        }
    }
}

internal typealias PolymorphicProvider = (className: String) -> DeserializationStrategy?




© 2015 - 2025 Weber Informatics LLC | Privacy Policy