Skip to content

Commit c7bcaf1

Browse files
authored
Add extension to access original descriptor from one made with SerialDescriptor.nullable (#2633)
It may be required when a custom class implementing SerialDescriptor is used for schema generation. Closes #2631
1 parent ddf44be commit c7bcaf1

File tree

3 files changed

+59
-0
lines changed

3 files changed

+59
-0
lines changed

core/api/kotlinx-serialization-core.api

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,7 @@ public final class kotlinx/serialization/descriptors/SerialDescriptorsKt {
308308
public static synthetic fun buildClassSerialDescriptor$default (Ljava/lang/String;[Lkotlinx/serialization/descriptors/SerialDescriptor;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlinx/serialization/descriptors/SerialDescriptor;
309309
public static final fun buildSerialDescriptor (Ljava/lang/String;Lkotlinx/serialization/descriptors/SerialKind;[Lkotlinx/serialization/descriptors/SerialDescriptor;Lkotlin/jvm/functions/Function1;)Lkotlinx/serialization/descriptors/SerialDescriptor;
310310
public static synthetic fun buildSerialDescriptor$default (Ljava/lang/String;Lkotlinx/serialization/descriptors/SerialKind;[Lkotlinx/serialization/descriptors/SerialDescriptor;Lkotlin/jvm/functions/Function1;ILjava/lang/Object;)Lkotlinx/serialization/descriptors/SerialDescriptor;
311+
public static final fun getNonNullOriginal (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/descriptors/SerialDescriptor;
311312
public static final fun getNullable (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/descriptors/SerialDescriptor;
312313
public static final fun listSerialDescriptor (Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/descriptors/SerialDescriptor;
313314
public static final fun mapSerialDescriptor (Lkotlinx/serialization/descriptors/SerialDescriptor;Lkotlinx/serialization/descriptors/SerialDescriptor;)Lkotlinx/serialization/descriptors/SerialDescriptor;

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,24 @@ public val SerialDescriptor.nullable: SerialDescriptor
223223
return SerialDescriptorForNullable(this)
224224
}
225225

226+
/**
227+
* Returns non-nullable serial descriptor for the type if this descriptor has been auto-generated (plugin
228+
* generated descriptors) or created with `.nullable` extension on a descriptor or serializer.
229+
*
230+
* Otherwise, returns this.
231+
*
232+
* It may return nullable descriptor if this descriptor has been created manually as nullable by directly implementing SerialDescriptor interface.
233+
*
234+
* @see SerialDescriptor.nullable
235+
* @see KSerializer.nullable
236+
*/
237+
@ExperimentalSerializationApi
238+
public val SerialDescriptor.nonNullOriginal: SerialDescriptor
239+
get() = when (this) {
240+
is SerialDescriptorForNullable -> original
241+
else -> this
242+
}
243+
226244
/**
227245
* Builder for [SerialDescriptor] for user-defined serializers.
228246
*

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

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,4 +94,44 @@ class SerialDescriptorBuilderTest {
9494
assertTrue(descriptor.isNullable)
9595
assertEquals("my.Simple?", descriptor.serialName)
9696
}
97+
98+
@Test
99+
fun testNonNullOriginal() {
100+
listOf(
101+
buildClassSerialDescriptor("my.Simple") {},
102+
Boolean.serializer().descriptor,
103+
String.serializer().descriptor,
104+
ListSerializer(Int.serializer()).descriptor,
105+
).forEach { originalDescriptor ->
106+
// Unwrapping original descriptor when it is not nullable should return the same descriptor (no-op operation)
107+
assertSame(originalDescriptor.nonNullOriginal, originalDescriptor)
108+
109+
// Unwrapping original descriptor when it is nullable should return the original descriptor
110+
assertSame(originalDescriptor.nullable.nonNullOriginal, originalDescriptor)
111+
}
112+
113+
// Unwrapping original descriptor of a custom nullable descriptor should return the same descriptor
114+
val customNullableDescriptor = CustomNullableDescriptor()
115+
assertSame(customNullableDescriptor.nonNullOriginal, customNullableDescriptor)
116+
117+
// Unwrapping original descriptor of a nullable field should return the original non-null descriptor
118+
assertSame(Type.serializer().descriptor.getElementDescriptor(0).nonNullOriginal, String.serializer().descriptor)
119+
120+
}
121+
122+
@Serializable
123+
class Type(val field: String?)
124+
125+
private class CustomNullableDescriptor : SerialDescriptor {
126+
override val isNullable: Boolean = true
127+
128+
override val serialName: String = "CustomNullableDescriptor"
129+
override val kind: SerialKind = PrimitiveKind.STRING
130+
override val elementsCount: Int = 0
131+
override fun getElementName(index: Int): String = error("Should not be called")
132+
override fun getElementIndex(name: String): Int = error("Should not be called")
133+
override fun isElementOptional(index: Int): Boolean = error("Should not be called")
134+
override fun getElementAnnotations(index: Int): List<Annotation> = error("Should not be called")
135+
override fun getElementDescriptor(index: Int): SerialDescriptor = error("Should not be called")
136+
}
97137
}

0 commit comments

Comments
 (0)