Skip to content

Commit

Permalink
Add annotations array parameter for sealed, polymorphic, and object s…
Browse files Browse the repository at this point in the history
…erialzers

Enables support of @SerialInfo class-level annotation on such classes

Fixes #708
Fixes #1240
  • Loading branch information
sandwwraith committed Aug 24, 2021
1 parent ff28e0f commit c4b7aff
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ public class ContextualSerializer<T : Any>(
public constructor(serializableClass: KClass<T>) : this(serializableClass, null, EMPTY_SERIALIZER_ARRAY)

public override val descriptor: SerialDescriptor =
buildSerialDescriptor("kotlinx.serialization.ContextualSerializer", SerialKind.CONTEXTUAL).withContext(serializableClass)
buildSerialDescriptor("kotlinx.serialization.ContextualSerializer", SerialKind.CONTEXTUAL) {
annotations = fallbackSerializer?.descriptor?.annotations.orEmpty()
}.withContext(serializableClass)

public override fun serialize(encoder: Encoder, value: T) {
encoder.encodeSerializableValue(serializer(encoder.serializersModule), value)
Expand Down
20 changes: 17 additions & 3 deletions core/commonMain/src/kotlinx/serialization/PolymorphicSerializer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,28 @@ import kotlin.reflect.*
*/
@OptIn(ExperimentalSerializationApi::class)
public class PolymorphicSerializer<T : Any>(override val baseClass: KClass<T>) : AbstractPolymorphicSerializer<T>() {
public override val descriptor: SerialDescriptor =

@PublishedApi // should we allow user access to this constructor?
internal constructor(
baseClass: KClass<T>,
classAnnotations: Array<Annotation>
) : this(baseClass) {
_descriptor.annotations = classAnnotations.asList()
}

private val _descriptor: SerialDescriptorImpl =
buildSerialDescriptor("kotlinx.serialization.Polymorphic", PolymorphicKind.OPEN) {
element("type", String.serializer().descriptor)
element(
"value",
buildSerialDescriptor("kotlinx.serialization.Polymorphic<${baseClass.simpleName}>", SerialKind.CONTEXTUAL)
buildSerialDescriptor(
"kotlinx.serialization.Polymorphic<${baseClass.simpleName}>",
SerialKind.CONTEXTUAL
)
)
}.withContext(baseClass)
} as SerialDescriptorImpl

public override val descriptor: SerialDescriptor = _descriptor.withContext(baseClass)

override fun toString(): String {
return "kotlinx.serialization.PolymorphicSerializer(baseClass: $baseClass)"
Expand Down
12 changes: 11 additions & 1 deletion core/commonMain/src/kotlinx/serialization/SealedSerializer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,17 @@ public class SealedClassSerializer<T : Any>(
subclassSerializers: Array<KSerializer<out T>>
) : AbstractPolymorphicSerializer<T>() {

@PublishedApi
internal constructor(
serialName: String,
baseClass: KClass<T>,
subclasses: Array<KClass<out T>>,
subclassSerializers: Array<KSerializer<out T>>,
classAnnotations: Array<Annotation>
) : this(serialName, baseClass, subclasses, subclassSerializers) {
(this.descriptor as SerialDescriptorImpl).annotations = classAnnotations.asList()
}

override val descriptor: SerialDescriptor = buildSerialDescriptor(serialName, PolymorphicKind.SEALED) {
element("type", String.serializer().descriptor)
val elementDescriptor =
Expand All @@ -87,7 +98,6 @@ public class SealedClassSerializer<T : Any>(
}
}
element("value", elementDescriptor)

}

private val class2Serializer: Map<KClass<out T>, KSerializer<out T>>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,8 @@ internal class SerialDescriptorImpl(
builder: ClassSerialDescriptorBuilder
) : SerialDescriptor, CachedNames {

override val annotations: List<Annotation> = builder.annotations
override var annotations: List<Annotation> = builder.annotations
internal set
override val serialNames: Set<String> = builder.elementNames.toHashSet()

private val elementNames: Array<String> = builder.elementNames.toTypedArray()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,15 @@ import kotlinx.serialization.encoding.*
@PublishedApi
@OptIn(ExperimentalSerializationApi::class)
internal class ObjectSerializer<T : Any>(serialName: String, private val objectInstance: T) : KSerializer<T> {
@PublishedApi
internal constructor(
serialName: String,
objectInstance: T,
classAnnotations: Array<Annotation>
) : this(serialName, objectInstance) {
(this.descriptor as SerialDescriptorImpl).annotations = classAnnotations.asList()
}

override val descriptor: SerialDescriptor = buildSerialDescriptor(serialName, StructureKind.OBJECT)

override fun serialize(encoder: Encoder, value: T) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,4 +81,40 @@ class SerialDescriptorAnnotationsTest {
}

private fun List<Annotation>.getCustom() = filterIsInstance<CustomAnnotation>().single().value

@Serializable
@CustomAnnotation("sealed")
sealed class Result {
@Serializable class OK(val s: String): Result()
}

@Serializable
@CustomAnnotation("abstract")
abstract class AbstractResult {
var result: String = ""
}

@Serializable
@CustomAnnotation("object")
object ObjectResult {}

@Serializable
class Holder(val r: Result, val a: AbstractResult, val o: ObjectResult, @Contextual val names: WithNames)

private fun doTest(position: Int, expected: String) {
val desc = Holder.serializer().descriptor.getElementDescriptor(position)
assertEquals(expected, desc.annotations.getCustom())
}

@Test
fun testCustomAnnotationOnSealedClass() = doTest(0, "sealed")

@Test
fun testCustomAnnotationOnPolymorphicClass() = doTest(1, "abstract")

@Test
fun testCustomAnnotationOnObject() = doTest(2, "object")

@Test
fun testCustomAnnotationTransparentForContextual() = doTest(3, "onClass")
}

0 comments on commit c4b7aff

Please sign in to comment.