Skip to content

Commit ada6ab1

Browse files
committed
Stabilize SerialDescriptor delegate builder
and allow creating primitive descriptors via it as well. This constructor function is a way to create a descriptor for your custom serializer if it simply delegates to an existing one. Since it fits its purpose and is unlikely to change in the future, we can promote it to stable API alongside other descriptor builders. Fixes #2547
1 parent df27589 commit ada6ab1

File tree

3 files changed

+51
-20
lines changed

3 files changed

+51
-20
lines changed

core/commonMain/src/kotlinx/serialization/descriptors/SerialDescriptors.kt

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,6 @@ import kotlin.reflect.*
4949
* }
5050
* ```
5151
*/
52-
@Suppress("FunctionName")
53-
@OptIn(ExperimentalSerializationApi::class)
5452
public fun buildClassSerialDescriptor(
5553
serialName: String,
5654
vararg typeParameters: SerialDescriptor,
@@ -69,7 +67,7 @@ public fun buildClassSerialDescriptor(
6967
}
7068

7169
/**
72-
* Factory to create a trivial primitive descriptors.
70+
* Factory to create trivial primitive descriptors. [serialName] must be non-blank and unique.
7371
* Primitive descriptors should be used when the serialized form of the data has a primitive form, for example:
7472
* ```
7573
* object LongAsStringSerializer : KSerializer<Long> {
@@ -86,16 +84,16 @@ public fun buildClassSerialDescriptor(
8684
* }
8785
* ```
8886
*/
87+
@Suppress("FunctionName")
8988
public fun PrimitiveSerialDescriptor(serialName: String, kind: PrimitiveKind): SerialDescriptor {
9089
require(serialName.isNotBlank()) { "Blank serial names are prohibited" }
9190
return PrimitiveDescriptorSafe(serialName, kind)
9291
}
9392

9493
/**
9594
* Factory to create a new descriptor that is identical to [original] except that the name is equal to [serialName].
96-
* Should be used when you want to serialize a type as another non-primitive type.
97-
* Don't use this if you want to serialize a type as a primitive value, use [PrimitiveSerialDescriptor] instead.
98-
*
95+
* Usually used when you want to serialize a type as another type, delegating implementation of `serialize` and `deserialize`.
96+
*
9997
* Example:
10098
* ```
10199
* @Serializable(CustomSerializer::class)
@@ -115,27 +113,24 @@ public fun PrimitiveSerialDescriptor(serialName: String, kind: PrimitiveKind): S
115113
* }
116114
* ```
117115
*/
118-
@ExperimentalSerializationApi
119116
public fun SerialDescriptor(serialName: String, original: SerialDescriptor): SerialDescriptor {
120117
require(serialName.isNotBlank()) { "Blank serial names are prohibited" }
121-
require(original.kind !is PrimitiveKind) { "For primitive descriptors please use 'PrimitiveSerialDescriptor' instead" }
122118
require(serialName != original.serialName) { "The name of the wrapped descriptor ($serialName) cannot be the same as the name of the original descriptor (${original.serialName})" }
123-
119+
if (original.kind is PrimitiveKind) checkNameIsNotAPrimitive(serialName)
120+
124121
return WrappedSerialDescriptor(serialName, original)
125122
}
126123

127-
@OptIn(ExperimentalSerializationApi::class)
128124
internal class WrappedSerialDescriptor(override val serialName: String, original: SerialDescriptor) : SerialDescriptor by original
129125

130126
/**
131127
* An unsafe alternative to [buildClassSerialDescriptor] that supports an arbitrary [SerialKind].
132128
* This function is left public only for migration of pre-release users and is not intended to be used
133-
* as generally-safe and stable mechanism. Beware that it can produce inconsistent or non spec-compliant instances.
129+
* as a generally safe and stable mechanism. Beware that it can produce inconsistent or non-spec-compliant instances.
134130
*
135-
* If you end up using this builder, please file an issue with your use-case in kotlinx.serialization issue tracker.
131+
* If you end up using this builder, please file an issue with your use-case to the kotlinx.serialization issue tracker.
136132
*/
137133
@InternalSerializationApi
138-
@OptIn(ExperimentalSerializationApi::class)
139134
public fun buildSerialDescriptor(
140135
serialName: String,
141136
kind: SerialKind,
@@ -152,14 +147,32 @@ public fun buildSerialDescriptor(
152147

153148
/**
154149
* Retrieves descriptor of type [T] using reified [serializer] function.
150+
*
151+
* Example:
152+
* ```
153+
* serialDescriptor<List<String>>() // Returns kotlin.collections.ArrayList(PrimitiveDescriptor(kotlin.String))
154+
* ```
155155
*/
156156
public inline fun <reified T> serialDescriptor(): SerialDescriptor = serializer<T>().descriptor
157157

158158
/**
159-
* Retrieves descriptor of type associated with the given [KType][type]
159+
* Retrieves descriptor of a type associated with the given [KType][type].
160+
*
161+
* Example:
162+
* ```
163+
* val type = typeOf<List<String>>()
164+
*
165+
* serialDescriptor(type) // Returns kotlin.collections.ArrayList(PrimitiveDescriptor(kotlin.String))
166+
* ```
160167
*/
161168
public fun serialDescriptor(type: KType): SerialDescriptor = serializer(type).descriptor
162169

170+
/* The rest of the functions intentionally left experimental for later stabilization
171+
It is unclear whether they should be left as-is,
172+
or moved to ClassSerialDescriptorBuilder (because this is the main place for them to be used),
173+
or simply deprecated in favor of ListSerializer(Element.serializer()).descriptor
174+
*/
175+
163176
/**
164177
* Creates a descriptor for the type `List<T>` where `T` is the type associated with [elementDescriptor].
165178
*/
@@ -227,9 +240,10 @@ public val SerialDescriptor.nullable: SerialDescriptor
227240
* Returns non-nullable serial descriptor for the type if this descriptor has been auto-generated (plugin
228241
* generated descriptors) or created with `.nullable` extension on a descriptor or serializer.
229242
*
230-
* Otherwise, returns this.
243+
* Otherwise, returns `this`.
231244
*
232-
* It may return nullable descriptor if this descriptor has been created manually as nullable by directly implementing SerialDescriptor interface.
245+
* It may return a nullable descriptor
246+
* if `this` descriptor has been created manually as nullable by directly implementing SerialDescriptor interface.
233247
*
234248
* @see SerialDescriptor.nullable
235249
* @see KSerializer.nullable

core/commonMain/src/kotlinx/serialization/internal/Primitives.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,15 @@ internal class PrimitiveSerialDescriptor(
3737
return false
3838
}
3939
override fun hashCode() = serialName.hashCode() + 31 * kind.hashCode()
40-
private fun error(): Nothing = throw IllegalStateException("Primitive descriptor does not have elements")
40+
private fun error(): Nothing = throw IllegalStateException("Primitive descriptor $serialName does not have elements")
4141
}
4242

4343
internal fun PrimitiveDescriptorSafe(serialName: String, kind: PrimitiveKind): SerialDescriptor {
44-
checkName(serialName)
44+
checkNameIsNotAPrimitive(serialName)
4545
return PrimitiveSerialDescriptor(serialName, kind)
4646
}
4747

48-
private fun checkName(serialName: String) {
48+
internal fun checkNameIsNotAPrimitive(serialName: String) {
4949
val values = BUILTIN_SERIALIZERS.values
5050
for (primitive in values) {
5151
val primitiveName = primitive.descriptor.serialName

core/commonTest/src/kotlinx/serialization/WrappedSerialDescriptorTest.kt

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,21 @@ class WrappedSerialDescriptorTest {
5858
fun testWrappedComplexClass() {
5959
checkWrapped(ComplexType.serializer().descriptor, "WrappedComplexType")
6060
}
61-
}
61+
62+
@Test
63+
fun testWrappedPrimitive() {
64+
checkWrapped(Int.serializer().descriptor, "MyInt")
65+
}
66+
67+
@Test
68+
fun testWrappedPrimitiveContract() {
69+
assertFails { SerialDescriptor(" ", ComplexType.serializer().descriptor) }
70+
assertFails {
71+
SerialDescriptor(
72+
SimpleType.serializer().descriptor.serialName,
73+
SimpleType.serializer().descriptor
74+
)
75+
}
76+
assertFails { SerialDescriptor("kotlin.Int", Int.serializer().descriptor) }
77+
}
78+
}

0 commit comments

Comments
 (0)