
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