From the docs there is built in support for contai...
# serialization
s
From the docs there is built in support for container classes ie. `Box`:
Copy code
@Serializable
@SerialName("Box")
class Box<T>(val contents: T)
I expected to be able to combine built in sealed classes and container support ie.:
Copy code
@Serializable
@SerialName("Either")
sealed class Either<L, R> {
    @Serializable
    class Left<L, R>(val d: L) : Either<L, R>()

    @Serializable
    class Right<L, R>(val d: R) : Either<L, R>()
}

@Serializable
data class SerTest(
    val either: Either<String, Int>
)

And used as:

    @Test
    fun foo() {
        val d = SerTest(Either.Left("left"))

        val str = Json.encodeToString(d)
        assertEquals("""{"either":{"d":"left"}}""", str)
    }
Fails with:
Copy code
kotlinx.serialization.SerializationException: Class 'String' is not registered for polymorphic serialization in the scope of 'Any'.
To be registered automatically, class 'String' has to be '@Serializable', and the base class 'Any' has to be sealed and '@Serializable'.
Alternatively, register the serializer for 'String' explicitly in a corresponding SerializersModule.
But I cannot get it to work. Do I have to write my own serializer or am I missing something ?
p
If you could provide an example of the code you actually use that would be useful, as well as any errors you get. Note that generic types add some additional complexity with the compile-time types used at serialization.
s
It is the actual code. I am trying to make my Either class serializable
p
What I mean is how do you use it? How do you call the serialization? Does
Json.encodeToString(Either.Left("Hello"))
work?
s
I have updated the original description
p
Ah, the problem is with polymorhpic serialization. The error message says that the erased generic type at the call site is
Any
. For security purposes the serialization library doesn't automatically serialize/deserialize subclasses for non-sealed parents. The solution is to either add the requirements to the serializers module (per the error), or to have more specific member types.
However, it is strange as the concrete types in
SerTest
is String/Int.
s
But it works fine for `Box`:
Copy code
@Serializable
data class BoxTest(
    val box: Box<String>
)

@Test
fun boxtest() {
    val d = BoxTest(Box("left"))

    val str = Json.encodeToString(d)
    assertEquals("""{"box":{"contents":"left"}}""", str)
}
I just wanted to know if there was any way to avoid having to manually create a
KSerializer
that implements this behavior which I expected to be quite standard and supported out of the box
I would love for this to be built in but this seems to work and looks fairly idiomatic:
Copy code
@OptIn(InternalSerializationApi::class)
class Either2Serializer<L, R>(
    lSerializer: KSerializer<L>,
    rSerializer: KSerializer<R>,
) : KSerializer<Either<L, R>> {

    private val leftSerializer = Either.Left.serializer(lSerializer, rSerializer)
    private val rightSerializer = Either.Right.serializer(lSerializer, rSerializer)

    @Suppress("UNCHECKED_CAST")
    private val impl = SealedClassSerializer(Either::class.qualifiedName!!,
        Either::class,
        arrayOf(Either.Left::class, Either.Right::class),
        arrayOf(leftSerializer, rightSerializer)) as SealedClassSerializer<Either<L, R>>

    override val descriptor
        get() = impl.descriptor

    override fun deserialize(decoder: Decoder): Either<L, R> = impl.deserialize(decoder)

    override fun serialize(encoder: Encoder, value: Either<L, R>) = impl.serialize(encoder, value)

}
p
This
EitherSerializer
is effectively a polymorphic serializer. This means there needs to be a way to mark left vs right. (The same way that nullability has a nullMarker). Unfortunately there is no standard approach for this that formats could then use to elide the marker if not needed, unless you actually use a polymorphic serializer.