diff --git a/core/api/kotlinx-serialization-core.api b/core/api/kotlinx-serialization-core.api index 9159db420..f55f65544 100644 --- a/core/api/kotlinx-serialization-core.api +++ b/core/api/kotlinx-serialization-core.api @@ -117,6 +117,7 @@ public final class kotlinx/serialization/SerializersKt { public static final fun serializer (Lkotlin/reflect/KType;)Lkotlinx/serialization/KSerializer; public static final fun serializer (Lkotlinx/serialization/modules/SerializersModule;Ljava/lang/reflect/Type;)Lkotlinx/serialization/KSerializer; public static final fun serializer (Lkotlinx/serialization/modules/SerializersModule;Lkotlin/reflect/KType;)Lkotlinx/serialization/KSerializer; + public static final fun serializerOrNull (Ljava/lang/Class;)Lkotlinx/serialization/KSerializer; public static final fun serializerOrNull (Ljava/lang/reflect/Type;)Lkotlinx/serialization/KSerializer; public static final fun serializerOrNull (Lkotlin/reflect/KClass;)Lkotlinx/serialization/KSerializer; public static final fun serializerOrNull (Lkotlin/reflect/KType;)Lkotlinx/serialization/KSerializer; diff --git a/core/commonMain/src/kotlinx/serialization/Annotations.kt b/core/commonMain/src/kotlinx/serialization/Annotations.kt index b7710a90e..196300755 100644 --- a/core/commonMain/src/kotlinx/serialization/Annotations.kt +++ b/core/commonMain/src/kotlinx/serialization/Annotations.kt @@ -294,6 +294,7 @@ public annotation class Polymorphic * * Implementing [SerialDescriptor] interfaces * * Not-yet-stable serialization formats that require additional polishing */ +@MustBeDocumented @Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION, AnnotationTarget.TYPEALIAS) @RequiresOptIn(level = RequiresOptIn.Level.WARNING) public annotation class ExperimentalSerializationApi @@ -305,6 +306,7 @@ public annotation class ExperimentalSerializationApi * and will be changed without any warnings or migration aids. * If you cannot avoid using internal API to solve your problem, please report your use-case to serialization's issue tracker. */ +@MustBeDocumented @Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION, AnnotationTarget.TYPEALIAS) @RequiresOptIn(level = RequiresOptIn.Level.ERROR) public annotation class InternalSerializationApi diff --git a/core/commonMain/src/kotlinx/serialization/Serializers.kt b/core/commonMain/src/kotlinx/serialization/Serializers.kt index 97a00e6a2..d24768f0c 100644 --- a/core/commonMain/src/kotlinx/serialization/Serializers.kt +++ b/core/commonMain/src/kotlinx/serialization/Serializers.kt @@ -137,7 +137,7 @@ internal fun SerializersModule.reflectiveOrContextual(kClass: KClass KClass.serializer(): KSerializer = serializerOrNull() * 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 consistent across different platforms or + * 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 `serializer()` implementation. + * 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 diff --git a/core/jvmMain/src/kotlinx/serialization/SerializersJvm.kt b/core/jvmMain/src/kotlinx/serialization/SerializersJvm.kt index 633836d35..9329c8703 100644 --- a/core/jvmMain/src/kotlinx/serialization/SerializersJvm.kt +++ b/core/jvmMain/src/kotlinx/serialization/SerializersJvm.kt @@ -74,6 +74,19 @@ public fun SerializersModule.serializer(type: Type): KSerializer = public fun SerializersModule.serializerOrNull(type: Type): KSerializer? = serializerByJavaTypeImpl(type, failOnMissingTypeArgSerializer = false) +/** + * Retrieves a [KSerializer] for the given [java.lang.Class] instance of the Kotlin type or returns `null` if none is found. + * The given class must be annotated with [Serializable] or be one of the built-in types. + * + * The API avoids instantiation of the correspnding [KClass][Class.kotlin] on the best-effort basis. + * + * This is a [Class] counterpart of [KClass.serializerOrNull] with all the restrictions the original function implies, + * including the general recommendation to avoid uses of this API. + */ +@InternalSerializationApi +public fun Class.serializerOrNull(): KSerializer? = + constructSerializerForGivenTypeArgs() ?: kotlin.builtinSerializerOrNull() + @OptIn(ExperimentalSerializationApi::class) private fun SerializersModule.serializerByJavaTypeImpl(type: Type, failOnMissingTypeArgSerializer: Boolean = true): KSerializer? = when (type) { diff --git a/core/jvmMain/src/kotlinx/serialization/internal/Platform.kt b/core/jvmMain/src/kotlinx/serialization/internal/Platform.kt index 74f4c4e0b..552ca5c0f 100644 --- a/core/jvmMain/src/kotlinx/serialization/internal/Platform.kt +++ b/core/jvmMain/src/kotlinx/serialization/internal/Platform.kt @@ -33,7 +33,7 @@ internal actual fun KClass.constructSerializerForGivenTypeArgs(vara } @Suppress("UNCHECKED_CAST") -private fun Class.constructSerializerForGivenTypeArgs(vararg args: KSerializer): KSerializer? { +internal fun Class.constructSerializerForGivenTypeArgs(vararg args: KSerializer): KSerializer? { if (isEnum && isNotAnnotated()) { return createEnumSerializer() } diff --git a/formats/json/jvmTest/src/kotlinx/serialization/features/SerializerByTypeTest.kt b/formats/json/jvmTest/src/kotlinx/serialization/features/SerializerByTypeTest.kt index 509c9e0c5..770813674 100644 --- a/formats/json/jvmTest/src/kotlinx/serialization/features/SerializerByTypeTest.kt +++ b/formats/json/jvmTest/src/kotlinx/serialization/features/SerializerByTypeTest.kt @@ -18,7 +18,7 @@ import kotlin.test.* class SerializerByTypeTest { - val json = Json { } + private val json = Json @Serializable data class Box(val a: T) @@ -61,6 +61,7 @@ class SerializerByTypeTest { val myArr = arrayOf("a", "b", "c") val token = myArr::class.java assertSerializedWithType(token, """["a","b","c"]""", myArr) + myArr::class.assertKClassSerializer() } @Test @@ -156,7 +157,6 @@ class SerializerByTypeTest { val myTriple = Triple("1", 2, Box(42)) val token = typeTokenOf>>() assertSerializedWithType(token, """{"first":"1","second":2,"third":{"a":42}}""", myTriple) - } @Test @@ -175,6 +175,7 @@ class SerializerByTypeTest { fun testNamedCompanion() { val namedCompanion = WithNamedCompanion(1) assertSerializedWithType(WithNamedCompanion::class.java, """{"a":1}""", namedCompanion) + WithNamedCompanion::class.assertKClassSerializer() } @Test @@ -182,6 +183,7 @@ class SerializerByTypeTest { val token = typeTokenOf() val serial = serializer(token) assertSame(Int.serializer() as KSerializer<*>, serial) + Int::class.assertKClassSerializer() } @Test @@ -189,6 +191,7 @@ class SerializerByTypeTest { val token = typeTokenOf() val serial = serializer(token) assertEquals(SerializableObject.serializer().descriptor, serial.descriptor) + SerializableObject::class.assertKClassSerializer() } @Suppress("UNCHECKED_CAST") @@ -274,4 +277,8 @@ class SerializerByTypeTest { serializer(typeTokenOf>()) } } + + private fun KClass.assertKClassSerializer() { + assertEquals(serializerOrNull(), java.serializerOrNull()) + } }