
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