Skip to content

Commit f451e43

Browse files
authored
Remove experimentality from serializer(java.lang.Type) function family (#2069)
As it is needed for third-part converter libraries. Revamp docs for all overloads of serializer().
1 parent 1675987 commit f451e43

File tree

4 files changed

+148
-58
lines changed

4 files changed

+148
-58
lines changed

core/commonMain/src/kotlinx/serialization/Serializers.kt

Lines changed: 77 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,54 +18,112 @@ import kotlin.reflect.*
1818

1919
/**
2020
* Retrieves a serializer for the given type [T].
21-
* This method is a reified version of `serializer(KType)`.
21+
* This overload is a reified version of `serializer(KType)`.
22+
*
23+
* This overload works with full type information, including type arguments and nullability,
24+
* and is a recommended way to retrieve a serializer.
25+
* For example, `serializer<List<String?>>()` returns [KSerializer] that is able
26+
* to serialize and deserialize list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`.
27+
*
28+
* Variance of [T]'s type arguments is not used by the serialization and is not taken into account.
29+
* Star projections in [T]'s type arguments are prohibited.
30+
*
31+
* @throws SerializationException if serializer cannot be created (provided [T] or its type argument is not serializable).
32+
* @throws IllegalArgumentException if any of [T]'s type arguments contains star projection
2233
*/
2334
public inline fun <reified T> serializer(): KSerializer<T> {
2435
return serializer(typeOf<T>()).cast()
2536
}
2637

2738
/**
28-
* Retrieves serializer for the given type [T] from the current [SerializersModule] and,
29-
* if not found, fallbacks to plain [serializer] method.
39+
* Retrieves default serializer for the given type [T] and,
40+
* if [T] is not serializable, fallbacks to [contextual][SerializersModule.getContextual] lookup.
41+
*
42+
* This overload works with full type information, including type arguments and nullability,
43+
* and is a recommended way to retrieve a serializer.
44+
* For example, `serializer<List<String?>>()` returns [KSerializer] that is able
45+
* to serialize and deserialize list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`.
46+
*
47+
* Variance of [T]'s type arguments is not used by the serialization and is not taken into account.
48+
* Star projections in [T]'s type arguments are prohibited.
49+
*
50+
* @throws SerializationException if serializer cannot be created (provided [T] or its type argument is not serializable).
51+
* @throws IllegalArgumentException if any of [T]'s type arguments contains star projection
3052
*/
3153
public inline fun <reified T> SerializersModule.serializer(): KSerializer<T> {
3254
return serializer(typeOf<T>()).cast()
3355
}
3456

3557
/**
3658
* Creates a serializer for the given [type].
37-
* [type] argument can be obtained with experimental [typeOf] method.
38-
* @throws SerializationException if serializer cannot be created (provided [type] or its type argument is not serializable).
59+
* [type] argument is usually obtained with [typeOf] method.
60+
*
61+
* This overload works with full type information, including type arguments and nullability,
62+
* and is a recommended way to retrieve a serializer.
63+
* For example, `serializer<typeOf<List<String?>>>()` returns [KSerializer] that is able
64+
* to serialize and deserialize list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`.
65+
*
66+
* Variance of [type]'s type arguments is not used by the serialization and is not taken into account.
67+
* Star projections in [type]'s arguments are prohibited.
68+
*
69+
* @throws SerializationException if serializer cannot be created (provided [type] or its type argument is not serializable).
70+
* @throws IllegalArgumentException if any of [type]'s arguments contains star projection
3971
*/
40-
@OptIn(ExperimentalSerializationApi::class)
4172
public fun serializer(type: KType): KSerializer<Any?> = EmptySerializersModule().serializer(type)
4273

4374
/**
44-
* Creates a serializer for the given [type].
45-
* [type] argument can be obtained with experimental [typeOf] method.
46-
* Returns `null` if serializer cannot be created (provided [type] or its type argument is not serializable).
75+
* Creates a serializer for the given [type] if possible.
76+
* [type] argument is usually obtained with [typeOf] method.
77+
*
78+
* This overload works with full type information, including type arguments and nullability,
79+
* and is a recommended way to retrieve a serializer.
80+
* For example, `serializerOrNull<typeOf<List<String?>>>()` returns [KSerializer] that is able
81+
* to serialize and deserialize list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`.
82+
*
83+
* Variance of [type]'s arguments is not used by the serialization and is not taken into account.
84+
* Star projections in [type]'s arguments are prohibited.
85+
*
86+
* @returns [KSerializer] for the given [type] or `null` if serializer cannot be created (given [type] or its type argument is not serializable).
87+
* @throws IllegalArgumentException if any of [type]'s arguments contains star projection
4788
*/
48-
@OptIn(ExperimentalSerializationApi::class)
4989
public fun serializerOrNull(type: KType): KSerializer<Any?>? = EmptySerializersModule().serializerOrNull(type)
5090

5191
/**
52-
* Attempts to create a serializer for the given [type] and fallbacks to [contextual][SerializersModule.getContextual]
53-
* lookup for non-serializable types.
54-
* [type] argument can be obtained with experimental [typeOf] method.
92+
* Retrieves default serializer for the given [type] and,
93+
* if [type] is not serializable, fallbacks to [contextual][SerializersModule.getContextual] lookup.
94+
* [type] argument is usually obtained with [typeOf] method.
95+
*
96+
* This overload works with full type information, including type arguments and nullability,
97+
* and is a recommended way to retrieve a serializer.
98+
* For example, `serializer<typeOf<List<String?>>>()` returns [KSerializer] that is able
99+
* to serialize and deserialize list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`.
100+
*
101+
* Variance of [type]'s arguments is not used by the serialization and is not taken into account.
102+
* Star projections in [type]'s arguments are prohibited.
103+
*
55104
* @throws SerializationException if serializer cannot be created (provided [type] or its type argument is not serializable and is not registered in [this] module).
105+
* @throws IllegalArgumentException if any of [type]'s arguments contains star projection
56106
*/
57-
@OptIn(ExperimentalSerializationApi::class)
58107
public fun SerializersModule.serializer(type: KType): KSerializer<Any?> =
59108
serializerByKTypeImpl(type, failOnMissingTypeArgSerializer = true) ?: type.kclass()
60109
.platformSpecificSerializerNotRegistered()
61110

62111
/**
63-
* Attempts to create a serializer for the given [type] and fallbacks to [contextual][SerializersModule.getContextual]
64-
* lookup for non-serializable types.
65-
* [type] argument can be obtained with experimental [typeOf] method.
66-
* Returns `null` if serializer cannot be created (provided [type] or its type argument is not serializable and is not registered in [this] module).
112+
* Retrieves default serializer for the given [type] and,
113+
* if [type] is not serializable, fallbacks to [contextual][SerializersModule.getContextual] lookup.
114+
* [type] argument is usually obtained with [typeOf] method.
115+
*
116+
* This overload works with full type information, including type arguments and nullability,
117+
* and is a recommended way to retrieve a serializer.
118+
* For example, `serializerOrNull<typeOf<List<String?>>>()` returns [KSerializer] that is able
119+
* to serialize and deserialize list of nullable strings — i.e. `ListSerializer(String.serializer().nullable)`.
120+
*
121+
* Variance of [type]'s arguments is not used by the serialization and is not taken into account.
122+
* Star projections in [type]'s arguments are prohibited.
123+
*
124+
* @returns [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).
125+
* @throws IllegalArgumentException if any of [type]'s arguments contains star projection
67126
*/
68-
@OptIn(ExperimentalSerializationApi::class)
69127
public fun SerializersModule.serializerOrNull(type: KType): KSerializer<Any?>? =
70128
serializerByKTypeImpl(type, failOnMissingTypeArgSerializer = false)
71129

@@ -189,7 +247,6 @@ private fun KClass<Any>.builtinParametrizedSerializer(
189247
typeArguments: List<KType>,
190248
serializers: List<KSerializer<Any?>>,
191249
): KSerializer<out Any>? {
192-
// Array is not supported, see KT-32839
193250
return when (this) {
194251
Collection::class, List::class, MutableList::class, ArrayList::class -> ArrayListSerializer(serializers[0])
195252
HashSet::class -> HashSetSerializer(serializers[0])

core/commonMain/src/kotlinx/serialization/modules/SerializersModuleBuilders.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public inline fun SerializersModule(builderAction: SerializersModuleBuilder.() -
3636
/**
3737
* A [SerializersModule] which is empty and returns `null` from each method.
3838
*/
39+
@Suppress("FunctionName")
3940
public fun EmptySerializersModule(): SerializersModule = @Suppress("DEPRECATION") EmptySerializersModule
4041

4142
/**

core/jvmMain/src/kotlinx/serialization/SerializersJvm.kt

Lines changed: 50 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -17,65 +17,86 @@ import java.lang.reflect.*
1717
import kotlin.reflect.*
1818

1919
/**
20-
* Reflectively constructs a serializer for the given reflective Java [type].
21-
* [serializer] is intended to be used as an interoperability layer for libraries like GSON and Retrofit,
22-
* that operate with reflective Java [Type] and cannot use [typeOf].
20+
* Reflectively retrieves a serializer for the given [type].
2321
*
24-
* For application-level serialization, it is recommended to use `serializer<T>()` instead as it is aware of
22+
* This overload is intended to be used as an interoperability layer for JVM-centric libraries,
23+
* that operate with Java's type tokens and cannot use Kotlin's [KType] or [typeOf].
24+
* For application-level serialization, it is recommended to use `serializer<T>()` or `serializer(KType)` instead as it is aware of
2525
* Kotlin-specific type information, such as nullability, sealed classes and object singletons.
2626
*
27+
* Note that because [Type] does not contain any information about nullability, all created serializers
28+
* work only with non-nullable data.
29+
*
30+
* Not all [Type] implementations are supported.
31+
* [type] must be an instance of [Class], [GenericArrayType], [ParameterizedType] or [WildcardType].
32+
*
2733
* @throws SerializationException if serializer cannot be created (provided [type] or its type argument is not serializable).
34+
* @throws IllegalArgumentException if an unsupported subclass of [Type] is provided.
2835
*/
29-
@ExperimentalSerializationApi
3036
public fun serializer(type: Type): KSerializer<Any> = EmptySerializersModule().serializer(type)
3137

3238
/**
33-
* Reflectively constructs a serializer for the given reflective Java [type].
34-
* [serializer] is intended to be used as an interoperability layer for libraries like GSON and Retrofit,
35-
* that operate with reflective Java [Type] and cannot use [typeOf].
39+
* Reflectively retrieves a serializer for the given [type].
3640
*
37-
* For application-level serialization, it is recommended to use `serializer<T>()` instead as it is aware of
41+
* This overload is intended to be used as an interoperability layer for JVM-centric libraries,
42+
* that operate with Java's type tokens and cannot use Kotlin's [KType] or [typeOf].
43+
* For application-level serialization, it is recommended to use `serializer<T>()` or `serializer(KType)` instead as it is aware of
3844
* Kotlin-specific type information, such as nullability, sealed classes and object singletons.
3945
*
40-
* Returns `null` if serializer cannot be created (provided [type] or its type argument is not serializable).
46+
* Note that because [Type] does not contain any information about nullability, all created serializers
47+
* work only with non-nullable data.
48+
*
49+
* Not all [Type] implementations are supported.
50+
* [type] must be an instance of [Class], [GenericArrayType], [ParameterizedType] or [WildcardType].
51+
*
52+
* @return [KSerializer] for given [type] or `null` if serializer cannot be created (given [type] or its type argument is not serializable).
53+
* @throws IllegalArgumentException if an unsupported subclass of [Type] is provided.
4154
*/
42-
@ExperimentalSerializationApi
4355
public fun serializerOrNull(type: Type): KSerializer<Any>? = EmptySerializersModule().serializerOrNull(type)
4456

4557
/**
46-
* Retrieves serializer for the given reflective Java [type] using
47-
* reflective construction and [contextual][SerializersModule.getContextual] lookup for non-serializable types.
48-
*
49-
* [serializer] is intended to be used as an interoperability layer for libraries like GSON and Retrofit,
50-
* that operate with reflective Java [Type] and cannot use [typeOf].
58+
* Retrieves a serializer for the given [type] using
59+
* reflective construction and [contextual][SerializersModule.getContextual] lookup as a fallback for non-serializable types.
5160
*
52-
* For application-level serialization, it is recommended to use `serializer<T>()` instead as it is aware of
61+
* This overload is intended to be used as an interoperability layer for JVM-centric libraries,
62+
* that operate with Java's type tokens and cannot use Kotlin's [KType] or [typeOf].
63+
* For application-level serialization, it is recommended to use `serializer<T>()` or `serializer(KType)` instead as it is aware of
5364
* Kotlin-specific type information, such as nullability, sealed classes and object singletons.
5465
*
66+
* Note that because [Type] does not contain any information about nullability, all created serializers
67+
* work only with non-nullable data.
68+
*
69+
* Not all [Type] implementations are supported.
70+
* [type] must be an instance of [Class], [GenericArrayType], [ParameterizedType] or [WildcardType].
71+
*
5572
* @throws SerializationException if serializer cannot be created (provided [type] or its type argument is not serializable).
73+
* @throws IllegalArgumentException if an unsupported subclass of [Type] is provided.
5674
*/
57-
@ExperimentalSerializationApi
5875
public fun SerializersModule.serializer(type: Type): KSerializer<Any> =
5976
serializerByJavaTypeImpl(type, failOnMissingTypeArgSerializer = true) ?: type.prettyClass()
6077
.serializerNotRegistered()
6178

6279
/**
63-
* Retrieves serializer for the given reflective Java [type] using
64-
* reflective construction and [contextual][SerializersModule.getContextual] lookup for non-serializable types.
65-
*
66-
* [serializer] is intended to be used as an interoperability layer for libraries like GSON and Retrofit,
67-
* that operate with reflective Java [Type] and cannot use [typeOf].
80+
* Retrieves a serializer for the given [type] using
81+
* reflective construction and [contextual][SerializersModule.getContextual] lookup as a fallback for non-serializable types.
6882
*
69-
* For application-level serialization, it is recommended to use `serializer<T>()` instead as it is aware of
83+
* This overload is intended to be used as an interoperability layer for JVM-centric libraries,
84+
* that operate with Java's type tokens and cannot use Kotlin's [KType] or [typeOf].
85+
* For application-level serialization, it is recommended to use `serializer<T>()` or `serializer(KType)` instead as it is aware of
7086
* Kotlin-specific type information, such as nullability, sealed classes and object singletons.
7187
*
72-
* Returns `null` if serializer cannot be created (provided [type] or its type argument is not serializable).
88+
* Note that because [Type] does not contain any information about nullability, all created serializers
89+
* work only with non-nullable data.
90+
*
91+
* Not all [Type] implementations are supported.
92+
* [type] must be an instance of [Class], [GenericArrayType], [ParameterizedType] or [WildcardType].
93+
*
94+
* @return [KSerializer] for given [type] or `null` if serializer cannot be created (given [type] or its type argument is not serializable).
95+
* @throws IllegalArgumentException if an unsupported subclass of [Type] is provided.
7396
*/
74-
@ExperimentalSerializationApi
7597
public fun SerializersModule.serializerOrNull(type: Type): KSerializer<Any>? =
7698
serializerByJavaTypeImpl(type, failOnMissingTypeArgSerializer = false)
7799

78-
@OptIn(ExperimentalSerializationApi::class)
79100
private fun SerializersModule.serializerByJavaTypeImpl(
80101
type: Type,
81102
failOnMissingTypeArgSerializer: Boolean = true
@@ -116,15 +137,13 @@ private fun SerializersModule.serializerByJavaTypeImpl(
116137
) as KSerializer<Any>
117138

118139
else -> {
119-
// probably we should deprecate this method because it can't differ nullable vs non-nullable types
120-
// since it uses Java TypeToken, not Kotlin one
121140
val varargs = argsSerializers.map { it as KSerializer<Any?> }
122141
reflectiveOrContextual(rootClass as Class<Any>, varargs)
123142
}
124143
}
125144
}
126145
is WildcardType -> serializerByJavaTypeImpl(type.upperBounds.first())
127-
else -> throw IllegalArgumentException("typeToken should be an instance of Class<?>, GenericArray, ParametrizedType or WildcardType, but actual type is $type ${type::class}")
146+
else -> throw IllegalArgumentException("type should be an instance of Class<?>, GenericArrayType, ParametrizedType or WildcardType, but actual argument $type has type ${type::class}")
128147
}
129148

130149
@OptIn(ExperimentalSerializationApi::class)
@@ -177,5 +196,5 @@ private fun Type.prettyClass(): Class<*> = when (val it = this) {
177196
is ParameterizedType -> it.rawType.prettyClass()
178197
is WildcardType -> it.upperBounds.first().prettyClass()
179198
is GenericArrayType -> it.genericComponentType.prettyClass()
180-
else -> throw IllegalArgumentException("typeToken should be an instance of Class<?>, GenericArray, ParametrizedType or WildcardType, but actual type is $it ${it::class}")
199+
else -> throw IllegalArgumentException("type should be an instance of Class<?>, GenericArrayType, ParametrizedType or WildcardType, but actual argument $it has type ${it::class}")
181200
}

formats/json-tests/commonTest/src/kotlinx/serialization/SerializersLookupTest.kt

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,20 @@ class SerializersLookupTest : JsonTestBase() {
101101
assertSerializedWithType<ArrayList<in Int>>("[1,2,3]", myList)
102102
}
103103

104+
@Test
105+
fun testStarProjectionsAreProhibited() {
106+
val expectedMessage = "Star projections in type arguments are not allowed"
107+
assertFailsWithMessage<IllegalArgumentException>(expectedMessage) {
108+
serializer<Box<*>>()
109+
}
110+
assertFailsWithMessage<IllegalArgumentException>(expectedMessage) {
111+
serializer(typeOf<Box<*>>())
112+
}
113+
assertFailsWithMessage<IllegalArgumentException>(expectedMessage) {
114+
serializerOrNull(typeOf<Box<*>>())
115+
}
116+
}
117+
104118
@Test
105119
fun testNullableTypes() {
106120
val myList: List<Int?> = listOf(1, null, 3)
@@ -120,6 +134,12 @@ class SerializersLookupTest : JsonTestBase() {
120134
assertSerializedWithType("""{"first":"1","second":2,"third":{"boxed":42}}""", myTriple)
121135
}
122136

137+
@Test
138+
fun testLookupDuration() = noLegacyJs {
139+
assertNotNull(serializerOrNull(typeOf<Duration>()))
140+
assertSame(Duration.serializer(), serializer<Duration>())
141+
}
142+
123143
@Test
124144
fun testCustomGeneric() = noLegacyJs {
125145
val intBox = Box(42)
@@ -259,13 +279,6 @@ class SerializersLookupTest : JsonTestBase() {
259279
}
260280
}
261281

262-
// TODO uncomment when Kotlin 1.7.20 is released
263-
// @Test
264-
// fun testLookupDuration() = noLegacyJs {
265-
// assertNotNull(serializerOrNull(typeOf<Duration>()))
266-
// assertSame(Duration.serializer(), serializer<Duration>())
267-
// }
268-
269282
private inline fun <reified T> assertSerializedWithType(
270283
expected: String,
271284
value: T,

0 commit comments

Comments
 (0)