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

commonMain.kotlinx.serialization.Serializers.kt Maven / Gradle / Ivy

There is a newer version: 1.8.0
Show newest version
/*
 * Copyright 2017-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
 */
@file:Suppress("DEPRECATION_ERROR", "UNCHECKED_CAST")
@file:JvmMultifileClass
@file:JvmName("SerializersKt")

package kotlinx.serialization

import kotlinx.serialization.builtins.*
import kotlinx.serialization.builtins.MapEntrySerializer
import kotlinx.serialization.builtins.PairSerializer
import kotlinx.serialization.builtins.TripleSerializer
import kotlinx.serialization.internal.*
import kotlinx.serialization.modules.*
import kotlin.jvm.*
import kotlin.reflect.*

/**
 * Retrieves a serializer for the given type [T].
 * This overload is a reified version of `serializer(KType)`.
 *
 * This overload works with full type information, including type arguments and nullability,
 * and is a recommended way to retrieve a serializer.
 * For example, `serializer>()` returns [KSerializer] that is able
 * to serialize and deserialize list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`.
 *
 * Variance of [T]'s type arguments is not used by the serialization and is not taken into account.
 * Star projections in [T]'s type arguments are prohibited.
 *
 * @throws SerializationException if serializer cannot be created (provided [T] or its type argument is not serializable).
 * @throws IllegalArgumentException if any of [T]'s type arguments contains star projection
 */
public inline fun  serializer(): KSerializer {
    return serializer(typeOf()).cast()
}

/**
 * Retrieves default serializer for the given type [T] and,
 * if [T] is not serializable, fallbacks to [contextual][SerializersModule.getContextual] lookup.
 *
 * This overload works with full type information, including type arguments and nullability,
 * and is a recommended way to retrieve a serializer.
 * For example, `serializer>()` returns [KSerializer] that is able
 * to serialize and deserialize list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`.
 *
 * Variance of [T]'s type arguments is not used by the serialization and is not taken into account.
 * Star projections in [T]'s type arguments are prohibited.
 *
 * @throws SerializationException if serializer cannot be created (provided [T] or its type argument is not serializable).
 * @throws IllegalArgumentException if any of [T]'s type arguments contains star projection
 */
public inline fun  SerializersModule.serializer(): KSerializer {
    return serializer(typeOf()).cast()
}

/**
 * Creates a serializer for the given [type].
 * [type] argument is usually obtained with [typeOf] method.
 *
 * This overload works with full type information, including type arguments and nullability,
 * and is a recommended way to retrieve a serializer.
 * For example, `serializer>>()` returns [KSerializer] that is able
 * to serialize and deserialize list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`.
 *
 * Variance of [type]'s type arguments is not used by the serialization and is not taken into account.
 * Star projections in [type]'s arguments are prohibited.
 *
 * @throws SerializationException if serializer cannot be created (provided [type] or its type argument is not serializable).
 * @throws IllegalArgumentException if any of [type]'s arguments contains star projection
 */
public fun serializer(type: KType): KSerializer = EmptySerializersModule().serializer(type)


/**
 * Retrieves serializer for the given [kClass].
 * This method uses platform-specific reflection available.
 *
 * If [kClass] is a parametrized type then it is necessary to pass serializers for generic parameters in the [typeArgumentsSerializers].
 * The nullability of returned serializer is specified using the [isNullable].
 *
 * Note that it is impossible to create an array serializer with this method,
 * as array serializer needs additional information: type token for an element type.
 * To create array serializer, use overload with [KType] or [ArraySerializer] directly.
 *
 * Caching on JVM platform is disabled for this function, so it may work slower than an overload with [KType].
 *
 * @throws SerializationException if serializer cannot be created (provided [kClass] or its type argument is not serializable)
 * @throws SerializationException if [kClass] is a `kotlin.Array`
 * @throws SerializationException if size of [typeArgumentsSerializers] does not match the expected generic parameters count
 */
@ExperimentalSerializationApi
public fun serializer(
    kClass: KClass<*>,
    typeArgumentsSerializers: List>,
    isNullable: Boolean
): KSerializer = EmptySerializersModule().serializer(kClass, typeArgumentsSerializers, isNullable)

/**
 * Creates a serializer for the given [type] if possible.
 * [type] argument is usually obtained with [typeOf] method.
 *
 * This overload works with full type information, including type arguments and nullability,
 * and is a recommended way to retrieve a serializer.
 * For example, `serializerOrNull>>()` returns [KSerializer] that is able
 * to serialize and deserialize list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`.
 *
 * Variance of [type]'s arguments is not used by the serialization and is not taken into account.
 * Star projections in [type]'s arguments are prohibited.
 *
 * @return [KSerializer] for the given [type] or `null` if serializer cannot be created (given [type] or its type argument is not serializable).
 * @throws IllegalArgumentException if any of [type]'s arguments contains star projection
 */
public fun serializerOrNull(type: KType): KSerializer? = EmptySerializersModule().serializerOrNull(type)

/**
 * Retrieves default serializer for the given [type] and,
 * if [type] is not serializable, fallbacks to [contextual][SerializersModule.getContextual] lookup.
 * [type] argument is usually obtained with [typeOf] method.
 *
 * This overload works with full type information, including type arguments and nullability,
 * and is a recommended way to retrieve a serializer.
 * For example, `serializer>>()` returns [KSerializer] that is able
 * to serialize and deserialize list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`.
 *
 * Variance of [type]'s arguments is not used by the serialization and is not taken into account.
 * Star projections in [type]'s arguments are prohibited.
 *
 * @throws SerializationException if serializer cannot be created (provided [type] or its type argument is not serializable and is not registered in [this] module).
 * @throws IllegalArgumentException if any of [type]'s arguments contains star projection
 */
public fun SerializersModule.serializer(type: KType): KSerializer =
    serializerByKTypeImpl(type, failOnMissingTypeArgSerializer = true) ?: type.kclass()
        .platformSpecificSerializerNotRegistered()


/**
 * Retrieves serializer for the given [kClass] and,
 * if [kClass] is not serializable, fallbacks to [contextual][SerializersModule.getContextual] lookup.
 * This method uses platform-specific reflection available.
 *
 * If [kClass] is a parametrized type then it is necessary to pass serializers for generic parameters in the [typeArgumentsSerializers].
 * The nullability of returned serializer is specified using the [isNullable].
 *
 * Note that it is impossible to create an array serializer with this method,
 * as array serializer needs additional information: type token for an element type.
 * To create array serializer, use overload with [KType] or [ArraySerializer] directly.
 *
 * Caching on JVM platform is disabled for this function, so it may work slower than an overload with [KType].
 *
 * @throws SerializationException if serializer cannot be created (provided [kClass] or its type argument is not serializable and is not registered in [this] module)
 * @throws SerializationException if [kClass] is a `kotlin.Array`
 * @throws SerializationException if size of [typeArgumentsSerializers] does not match the expected generic parameters count
 */
@ExperimentalSerializationApi
public fun SerializersModule.serializer(
    kClass: KClass<*>,
    typeArgumentsSerializers: List>,
    isNullable: Boolean
): KSerializer =
    serializerByKClassImpl(kClass as KClass, typeArgumentsSerializers as List>, isNullable)
        ?: kClass.platformSpecificSerializerNotRegistered()

/**
 * Retrieves default serializer for the given [type] and,
 * if [type] is not serializable, fallbacks to [contextual][SerializersModule.getContextual] lookup.
 * [type] argument is usually obtained with [typeOf] method.
 *
 * This overload works with full type information, including type arguments and nullability,
 * and is a recommended way to retrieve a serializer.
 * For example, `serializerOrNull>>()` returns [KSerializer] that is able
 * to serialize and deserialize list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`.
 *
 * Variance of [type]'s arguments is not used by the serialization and is not taken into account.
 * Star projections in [type]'s arguments are prohibited.
 *
 * @return [KSerializer] for the given [type] or `null` if serializer cannot be created (given [type] or its type argument is not serializable and is not registered in [this] module).
 * @throws IllegalArgumentException if any of [type]'s arguments contains star projection
 */
public fun SerializersModule.serializerOrNull(type: KType): KSerializer? =
    serializerByKTypeImpl(type, failOnMissingTypeArgSerializer = false)

@OptIn(ExperimentalSerializationApi::class)
private fun SerializersModule.serializerByKTypeImpl(
    type: KType,
    failOnMissingTypeArgSerializer: Boolean
): KSerializer? {
    val rootClass = type.kclass()
    val isNullable = type.isMarkedNullable
    val typeArguments = type.arguments.map(KTypeProjection::typeOrThrow)

    val cachedSerializer  = if (typeArguments.isEmpty()) {
        if (rootClass.isInterface() && getContextual(rootClass) != null) {
            // We cannot use cache because it may be contextual non-sealed interface serializer,
            // but we cannot return result of getContextual() directly either, because rootClass
            // can be a sealed interface as well (in that case, rootClass.serializerOrNull() should have priority over getContextual()).
            // If we had function like KClass.isNonSealedInterface() we could optimize this place,
            // but Native does not provide enough reflection for that. (https://youtrack.jetbrains.com/issue/KT-41339)
            null
        } else {
            findCachedSerializer(rootClass, isNullable)
        }
    } else {
        // We cannot enable cache even if the current class is non-interface, as it may have interface among type arguments
        // and we do not want to waste time scanning them all.
        if (hasInterfaceContextualSerializers) {
            null
        } else {
            findParametrizedCachedSerializer(
                rootClass,
                typeArguments,
                isNullable
            ).getOrNull()
        }
    }

    if (cachedSerializer != null) return cachedSerializer

    // slow path to find contextual serializers in serializers module
    val contextualSerializer: KSerializer? = if (typeArguments.isEmpty()) {
        rootClass.serializerOrNull()
            ?: getContextual(rootClass)
            ?: rootClass.polymorphicIfInterface()
    } else {
        val serializers = serializersForParameters(typeArguments, failOnMissingTypeArgSerializer) ?: return null
        // first, we look among the built-in serializers, because the parameter could be contextual
        rootClass.parametrizedSerializerOrNull(serializers) { typeArguments[0].classifier }
            ?: getContextual(rootClass, serializers)
            // PolymorphicSerializer always is returned even for Interface, although it rarely works as expected.
            ?: rootClass.polymorphicIfInterface()
    }
    return contextualSerializer?.cast()?.nullable(isNullable)
}

@OptIn(ExperimentalSerializationApi::class)
private fun SerializersModule.serializerByKClassImpl(
    rootClass: KClass,
    typeArgumentsSerializers: List>,
    isNullable: Boolean
): KSerializer? {
    val serializer = if (typeArgumentsSerializers.isEmpty()) {
        rootClass.serializerOrNull() ?: getContextual(rootClass)
    } else {
        try {
            rootClass.parametrizedSerializerOrNull(typeArgumentsSerializers) {
                throw SerializationException("It is not possible to retrieve an array serializer using KClass alone, use KType instead or ArraySerializer factory")
            } ?: getContextual(
                rootClass,
                typeArgumentsSerializers
            )
        } catch (e: IndexOutOfBoundsException) {
            throw SerializationException("Unable to retrieve a serializer, the number of passed type serializers differs from the actual number of generic parameters", e)
        }
    }

    return serializer?.cast()?.nullable(isNullable)
}

/**
 * Returns null only if `failOnMissingTypeArgSerializer == false` and at least one parameter serializer not found.
 */
internal fun SerializersModule.serializersForParameters(
    typeArguments: List,
    failOnMissingTypeArgSerializer: Boolean
): List>? {
    val serializers = if (failOnMissingTypeArgSerializer) {
        typeArguments.map { serializer(it) }
    } else {
        typeArguments.map { serializerOrNull(it) ?: return null }
    }
    return serializers
}

/**
 * Retrieves a [KSerializer] for the given [KClass].
 * The given class must be annotated with [Serializable] or be one of the built-in types.
 *
 * This method uses platform-specific reflection available for the given erased `KClass`
 * and is not recommended to use this method for anything, but last-ditch resort, e.g.
 * when all type info is lost, your application has crashed and it is the final attempt to log or send some serializable data.
 *
 * The recommended way to retrieve the serializer is inline [serializer] function and [`serializer(KType)`][serializer]
 *
 * This API is not guaranteed to work consistently across different platforms or
 * to work in cases that slightly differ from "plain @Serializable class" and have platform and reflection specific limitations.
 *
 * ### Constraints
 * This paragraph explains known (but not all!) constraints of the `serializer()` implementation.
 * Please note that they are not bugs, but implementation restrictions that we cannot workaround.
 *
 * * This method may behave differently on JVM, JS and Native because of runtime reflection differences
 * * Serializers for classes with generic parameters are ignored by this method
 * * External serializers generated with `Serializer(forClass = )` are not lookuped consistently
 * * Serializers for classes with named companion objects  are not lookuped consistently
 *
 * @throws SerializationException if serializer can't be found.
 */
@InternalSerializationApi
public fun  KClass.serializer(): KSerializer = serializerOrNull() ?: serializerNotRegistered()

/**
 * Retrieves a [KSerializer] for the given [KClass] or returns `null` if none is found.
 * The given class must be annotated with [Serializable] or be one of the built-in types.
 * This method uses platform-specific reflection available for the given erased `KClass`
 * and it is not recommended to use this method for anything, but last-ditch resort, e.g.
 * when all type info is lost, your application has crashed and it is the final attempt to log or send some serializable data.
 *
 * This API is not guaranteed to work consistently across different platforms or
 * to work in cases that slightly differ from "plain @Serializable class".
 *
 * ### Constraints
 * This paragraph explains known (but not all!) constraints of the `serializerOrNull()` implementation.
 * Please note that they are not bugs, but implementation restrictions that we cannot workaround.
 *
 * * This method may behave differently on JVM, JS and Native because of runtime reflection differences
 * * Serializers for classes with generic parameters are ignored by this method
 * * External serializers generated with `Serializer(forClass = )` are not lookuped consistently
 * * Serializers for classes with named companion objects  are not lookuped consistently
 */
@InternalSerializationApi
public fun  KClass.serializerOrNull(): KSerializer? =
    compiledSerializerImpl() ?: builtinSerializerOrNull()

internal fun KClass.parametrizedSerializerOrNull(
    serializers: List>,
    elementClassifierIfArray: () -> KClassifier?
): KSerializer? {
    // builtin first because some standard parametrized interfaces (e.g. Map) must use builtin serializer but not polymorphic
    return builtinParametrizedSerializer(serializers, elementClassifierIfArray) ?: compiledParametrizedSerializer(serializers)
}


private fun KClass.compiledParametrizedSerializer(serializers: List>): KSerializer? {
    return constructSerializerForGivenTypeArgs(*serializers.toTypedArray())
}

@OptIn(ExperimentalSerializationApi::class)
private fun KClass.builtinParametrizedSerializer(
    serializers: List>,
    elementClassifierIfArray: () -> KClassifier?
): KSerializer? {
    return when (this) {
        Collection::class, List::class, MutableList::class, ArrayList::class -> ArrayListSerializer(serializers[0])
        HashSet::class -> HashSetSerializer(serializers[0])
        Set::class, MutableSet::class, LinkedHashSet::class -> LinkedHashSetSerializer(serializers[0])
        HashMap::class -> HashMapSerializer(serializers[0], serializers[1])
        Map::class, MutableMap::class, LinkedHashMap::class -> LinkedHashMapSerializer(
            serializers[0],
            serializers[1]
        )

        Map.Entry::class -> MapEntrySerializer(serializers[0], serializers[1])
        Pair::class -> PairSerializer(serializers[0], serializers[1])
        Triple::class -> TripleSerializer(serializers[0], serializers[1], serializers[2])
        else -> {
            if (isReferenceArray(this)) {
                ArraySerializer(elementClassifierIfArray() as KClass, serializers[0])
            } else {
                null
            }
        }
    }
}

private fun  KSerializer.nullable(shouldBeNullable: Boolean): KSerializer {
    if (shouldBeNullable) return nullable
    return this as KSerializer
}


/**
 * Overloads of [noCompiledSerializer] should never be called directly.
 * Instead, compiler inserts calls to them when intrinsifying [serializer] function.
 *
 * If no serializer has been found in compile time, call to [noCompiledSerializer] inserted instead.
 */
@Suppress("unused")
@PublishedApi
internal fun noCompiledSerializer(forClass: String): KSerializer<*> =
    throw SerializationException(notRegisteredMessage(forClass))

// Used when compiler intrinsic is inserted
@OptIn(ExperimentalSerializationApi::class)
@Suppress("unused")
@PublishedApi
internal fun noCompiledSerializer(module: SerializersModule, kClass: KClass<*>): KSerializer<*> {
    return module.getContextual(kClass) ?: kClass.serializerNotRegistered()
}

@OptIn(ExperimentalSerializationApi::class)
@Suppress("unused")
@PublishedApi
internal fun noCompiledSerializer(
    module: SerializersModule,
    kClass: KClass<*>,
    argSerializers: Array>
): KSerializer<*> {
    return module.getContextual(kClass, argSerializers.asList()) ?: kClass.serializerNotRegistered()
}

/**
 * Overloads of [moduleThenPolymorphic] should never be called directly.
 * Instead, compiler inserts calls to them when intrinsifying [serializer] function.
 *
 * If no request KClass is an interface, plugin performs call to [moduleThenPolymorphic] to achieve special behavior for interface serializers.
 * (They are only serializers that have module priority over default [PolymorphicSerializer]).
 */
@OptIn(ExperimentalSerializationApi::class)
@Suppress("unused")
@PublishedApi
internal fun moduleThenPolymorphic(module: SerializersModule, kClass: KClass<*>): KSerializer<*> {
    return module.getContextual(kClass) ?: PolymorphicSerializer(kClass)
}

@OptIn(ExperimentalSerializationApi::class)
@Suppress("unused")
@PublishedApi
internal fun moduleThenPolymorphic(module: SerializersModule, kClass: KClass<*>, argSerializers: Array>): KSerializer<*> {
    return module.getContextual(kClass, argSerializers.asList()) ?: PolymorphicSerializer(kClass)
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy