diff options
Diffstat (limited to 'core/jvmMain/src/kotlinx/serialization/SerializersJvm.kt')
-rw-r--r-- | core/jvmMain/src/kotlinx/serialization/SerializersJvm.kt | 107 |
1 files changed, 69 insertions, 38 deletions
diff --git a/core/jvmMain/src/kotlinx/serialization/SerializersJvm.kt b/core/jvmMain/src/kotlinx/serialization/SerializersJvm.kt index b110f121..b2d8da7c 100644 --- a/core/jvmMain/src/kotlinx/serialization/SerializersJvm.kt +++ b/core/jvmMain/src/kotlinx/serialization/SerializersJvm.kt @@ -17,65 +17,90 @@ import java.lang.reflect.* import kotlin.reflect.* /** - * Reflectively constructs a serializer for the given reflective Java [type]. - * [serializer] is intended to be used as an interoperability layer for libraries like GSON and Retrofit, - * that operate with reflective Java [Type] and cannot use [typeOf]. + * Reflectively retrieves a serializer for the given [type]. * - * For application-level serialization, it is recommended to use `serializer<T>()` instead as it is aware of + * This overload is intended to be used as an interoperability layer for JVM-centric libraries, + * that operate with Java's type tokens and cannot use Kotlin's [KType] or [typeOf]. + * For application-level serialization, it is recommended to use `serializer<T>()` or `serializer(KType)` instead as it is aware of * Kotlin-specific type information, such as nullability, sealed classes and object singletons. * + * Note that because [Type] does not contain any information about nullability, all created serializers + * work only with non-nullable data. + * + * Not all [Type] implementations are supported. + * [type] must be an instance of [Class], [GenericArrayType], [ParameterizedType] or [WildcardType]. + * * @throws SerializationException if serializer cannot be created (provided [type] or its type argument is not serializable). + * @throws IllegalArgumentException if an unsupported subclass of [Type] is provided. */ -@ExperimentalSerializationApi -public fun serializer(type: Type): KSerializer<Any> = EmptySerializersModule.serializer(type) +public fun serializer(type: Type): KSerializer<Any> = EmptySerializersModule().serializer(type) /** - * Reflectively constructs a serializer for the given reflective Java [type]. - * [serializer] is intended to be used as an interoperability layer for libraries like GSON and Retrofit, - * that operate with reflective Java [Type] and cannot use [typeOf]. + * Reflectively retrieves a serializer for the given [type]. * - * For application-level serialization, it is recommended to use `serializer<T>()` instead as it is aware of + * This overload is intended to be used as an interoperability layer for JVM-centric libraries, + * that operate with Java's type tokens and cannot use Kotlin's [KType] or [typeOf]. + * For application-level serialization, it is recommended to use `serializer<T>()` or `serializer(KType)` instead as it is aware of * Kotlin-specific type information, such as nullability, sealed classes and object singletons. * - * Returns `null` if serializer cannot be created (provided [type] or its type argument is not serializable). + * Note that because [Type] does not contain any information about nullability, all created serializers + * work only with non-nullable data. + * + * Not all [Type] implementations are supported. + * [type] must be an instance of [Class], [GenericArrayType], [ParameterizedType] or [WildcardType]. + * + * @return [KSerializer] for given [type] or `null` if serializer cannot be created (given [type] or its type argument is not serializable). + * @throws IllegalArgumentException if an unsupported subclass of [Type] is provided. */ -@ExperimentalSerializationApi -public fun serializerOrNull(type: Type): KSerializer<Any>? = EmptySerializersModule.serializerOrNull(type) +public fun serializerOrNull(type: Type): KSerializer<Any>? = EmptySerializersModule().serializerOrNull(type) /** - * Retrieves serializer for the given reflective Java [type] using - * reflective construction and [contextual][SerializersModule.getContextual] lookup for non-serializable types. + * Retrieves a serializer for the given [type] using + * reflective construction and [contextual][SerializersModule.getContextual] lookup as a fallback for non-serializable types. * - * [serializer] is intended to be used as an interoperability layer for libraries like GSON and Retrofit, - * that operate with reflective Java [Type] and cannot use [typeOf]. - * - * For application-level serialization, it is recommended to use `serializer<T>()` instead as it is aware of + * This overload is intended to be used as an interoperability layer for JVM-centric libraries, + * that operate with Java's type tokens and cannot use Kotlin's [KType] or [typeOf]. + * For application-level serialization, it is recommended to use `serializer<T>()` or `serializer(KType)` instead as it is aware of * Kotlin-specific type information, such as nullability, sealed classes and object singletons. * + * Note that because [Type] does not contain any information about nullability, all created serializers + * work only with non-nullable data. + * + * Not all [Type] implementations are supported. + * [type] must be an instance of [Class], [GenericArrayType], [ParameterizedType] or [WildcardType]. + * * @throws SerializationException if serializer cannot be created (provided [type] or its type argument is not serializable). + * @throws IllegalArgumentException if an unsupported subclass of [Type] is provided. */ -@ExperimentalSerializationApi public fun SerializersModule.serializer(type: Type): KSerializer<Any> = - serializerByJavaTypeImpl(type, failOnMissingTypeArgSerializer = true) ?: type.prettyClass().serializerNotRegistered() + serializerByJavaTypeImpl(type, failOnMissingTypeArgSerializer = true) + ?: type.prettyClass().serializerNotRegistered() /** - * Retrieves serializer for the given reflective Java [type] using - * reflective construction and [contextual][SerializersModule.getContextual] lookup for non-serializable types. + * Retrieves a serializer for the given [type] using + * reflective construction and [contextual][SerializersModule.getContextual] lookup as a fallback for non-serializable types. * - * [serializer] is intended to be used as an interoperability layer for libraries like GSON and Retrofit, - * that operate with reflective Java [Type] and cannot use [typeOf]. - * - * For application-level serialization, it is recommended to use `serializer<T>()` instead as it is aware of + * This overload is intended to be used as an interoperability layer for JVM-centric libraries, + * that operate with Java's type tokens and cannot use Kotlin's [KType] or [typeOf]. + * For application-level serialization, it is recommended to use `serializer<T>()` or `serializer(KType)` instead as it is aware of * Kotlin-specific type information, such as nullability, sealed classes and object singletons. * - * Returns `null` if serializer cannot be created (provided [type] or its type argument is not serializable). + * Note that because [Type] does not contain any information about nullability, all created serializers + * work only with non-nullable data. + * + * Not all [Type] implementations are supported. + * [type] must be an instance of [Class], [GenericArrayType], [ParameterizedType] or [WildcardType]. + * + * @return [KSerializer] for given [type] or `null` if serializer cannot be created (given [type] or its type argument is not serializable). + * @throws IllegalArgumentException if an unsupported subclass of [Type] is provided. */ -@ExperimentalSerializationApi public fun SerializersModule.serializerOrNull(type: Type): KSerializer<Any>? = serializerByJavaTypeImpl(type, failOnMissingTypeArgSerializer = false) -@OptIn(ExperimentalSerializationApi::class) -private fun SerializersModule.serializerByJavaTypeImpl(type: Type, failOnMissingTypeArgSerializer: Boolean = true): KSerializer<Any>? = +private fun SerializersModule.serializerByJavaTypeImpl( + type: Type, + failOnMissingTypeArgSerializer: Boolean = true +): KSerializer<Any>? = when (type) { is GenericArrayType -> { genericArraySerializer(type, failOnMissingTypeArgSerializer) @@ -85,7 +110,9 @@ private fun SerializersModule.serializerByJavaTypeImpl(type: Type, failOnMissing val rootClass = (type.rawType as Class<*>) val args = (type.actualTypeArguments) val argsSerializers = - if (failOnMissingTypeArgSerializer) args.map { serializer(it) } else args.map { serializerOrNull(it) ?: return null } + if (failOnMissingTypeArgSerializer) args.map { serializer(it) } else args.map { + serializerOrNull(it) ?: return null + } when { Set::class.java.isAssignableFrom(rootClass) -> SetSerializer(argsSerializers[0]) as KSerializer<Any> List::class.java.isAssignableFrom(rootClass) || Collection::class.java.isAssignableFrom(rootClass) -> ListSerializer( @@ -110,19 +137,20 @@ private fun SerializersModule.serializerByJavaTypeImpl(type: Type, failOnMissing ) as KSerializer<Any> else -> { - // probably we should deprecate this method because it can't differ nullable vs non-nullable types - // since it uses Java TypeToken, not Kotlin one val varargs = argsSerializers.map { it as KSerializer<Any?> } reflectiveOrContextual(rootClass as Class<Any>, varargs) } } } is WildcardType -> serializerByJavaTypeImpl(type.upperBounds.first()) - else -> throw IllegalArgumentException("typeToken should be an instance of Class<?>, GenericArray, ParametrizedType or WildcardType, but actual type is $type ${type::class}") + else -> throw IllegalArgumentException("type should be an instance of Class<?>, GenericArrayType, ParametrizedType or WildcardType, but actual argument $type has type ${type::class}") } @OptIn(ExperimentalSerializationApi::class) -private fun SerializersModule.typeSerializer(type: Class<*>, failOnMissingTypeArgSerializer: Boolean): KSerializer<Any>? { +private fun SerializersModule.typeSerializer( + type: Class<*>, + failOnMissingTypeArgSerializer: Boolean +): KSerializer<Any>? { return if (type.isArray && !type.componentType.isPrimitive) { val eType: Class<*> = type.componentType val s = if (failOnMissingTypeArgSerializer) serializer(eType) else (serializerOrNull(eType) ?: return null) @@ -134,7 +162,10 @@ private fun SerializersModule.typeSerializer(type: Class<*>, failOnMissingTypeAr } @OptIn(ExperimentalSerializationApi::class) -private fun <T : Any> SerializersModule.reflectiveOrContextual(jClass: Class<T>, typeArgumentsSerializers: List<KSerializer<Any?>>): KSerializer<T>? { +private fun <T : Any> SerializersModule.reflectiveOrContextual( + jClass: Class<T>, + typeArgumentsSerializers: List<KSerializer<Any?>> +): KSerializer<T>? { jClass.constructSerializerForGivenTypeArgs(*typeArgumentsSerializers.toTypedArray())?.let { return it } val kClass = jClass.kotlin return kClass.builtinSerializerOrNull() ?: getContextual(kClass, typeArgumentsSerializers) @@ -165,5 +196,5 @@ private fun Type.prettyClass(): Class<*> = when (val it = this) { is ParameterizedType -> it.rawType.prettyClass() is WildcardType -> it.upperBounds.first().prettyClass() is GenericArrayType -> it.genericComponentType.prettyClass() - else -> throw IllegalArgumentException("typeToken should be an instance of Class<?>, GenericArray, ParametrizedType or WildcardType, but actual type is $it ${it::class}") + else -> throw IllegalArgumentException("type should be an instance of Class<?>, GenericArrayType, ParametrizedType or WildcardType, but actual argument $it has type ${it::class}") } |