Skip to content

Combining SealedClassSerializers #1865

@Whathecode

Description

@Whathecode

What is your use-case and why do you need this feature?

I have a base, non-sealed, interface, from which sealed classes extend. At runtime (this is a generic framework), I need to be able to create a serializer which can handle all subclasses of multiple sealed classes that extend from this common interface.

My initial expectation was that I could instantiate a new SealedClassSerializer and pass all subclasses and subclassSerializers from the other serializers as constructor parameters. However, I found out that after instantiating SealedClassSerializer this information is no longer accessible (it is stored in a private class2Serializer field).

As an example, my specific use case: I have an IntegrationEvent interface and application services which each define the events they emit by defining a sealed class which extends from this interface. Application services can subscribe to events from other services. Serializing any event received from dependent services thus requires me to be able to deserialize any of the events defined as subclasses for each of the sealed event classes.

P.s. I understand SealedClassSerializer is an internal serialization API; I find myself accessing internal APIs quite frequently since I'm using kotlinx.serialization in quite elaborate use cases. I hope sharing these may inspire some of the internal APIs to be stabilized and made public, since I would argue these are valid use cases. For now, I don't mind using them as unstable APIs.

Describe the solution you'd like

Could subclasses and subclassSerializers, or a meaningful map thereof, be made public as readonly fields? Once you have access to this class, I don't think information hiding makes sense. In addition, maybe a factory method can be added which takes multiple other sealed class serializers to combine them.

I currently hack this using JVM reflection as workaround:

// Get event serializer capable of serializing published events, as well as events the service subscribes to.
val eventSerializers = dependentServices.plus( serviceKlass ).map { getEventSerializer( it ) }
// HACK: There is no public accessor to get the subclass serializers. There probably should be.
val subclassesField = SealedClassSerializer::class.java
    .getDeclaredField( "class2Serializer" )
    .apply { isAccessible = true }
val allSubclassSerializers = eventSerializers.flatMap {
    @Suppress( "UNCHECKED_CAST" )
    val serializers = subclassesField.get( it )
        as Map<KClass<IntegrationEvent<*>>, KSerializer<IntegrationEvent<*>>>
    serializers.toList()
}.toMap()
eventSerializer = SealedClassSerializer(
    IntegrationEvent::class.qualifiedName!!,
    IntegrationEvent::class,
    allSubclassSerializers.keys.toTypedArray(),
    allSubclassSerializers.values.toTypedArray()
)

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions