Hi, how can Use `JsonContentPolymorphicSerializer`...
# serialization
o
Hi, how can Use
JsonContentPolymorphicSerializer
with a collection? 🧵
My use case is this: json data:
{ "data":  ["a", "b"] }
(list of strings) or
{ "data":  [{"foo": 1}, {"foo": 2}] }
(list of object) So I created a
JsonContentPolymorphicSerializer
:
Copy code
@Serializable
sealed interface Element {
    @Serializable
    class ObjectValue(val value: MyClass): Element
    @JvmInline @Serializable
    value class StringValue(val value: String): Element
}

object MySerializer : JsonContentPolymorphicSerializer<Element>(
    Element::class
) {
    override fun selectDeserializer(element: JsonElement) =
        when (element) {
            is JsonObject -> Element.ObjectValue.serializer()
            else -> Element.StringValue.serializer()
        }
}

data class A(
     @Serializable(with = /* can't use my serializer here */) //doesn't work
     val data: List<Element>
)
g
@Serializable on Element is generating a serializer that you don't want. Why not defining
@Serializable(with=MySerializer::class) sealed interface Element
instead?
Also I'm not sure you want a Polymorphic there. You want to serialize the full list, because else you could have either a string of your object 🤔 If that's the case, your annotation location is right, but you should serialize a List<Element> I believe.
o
thanks for showing me the right path @glureau. what I did was this:
Copy code
@Serializable(with = MySerializer::class)
sealed interface Element

@Serializable
data class MyClass(.....): Element

@JvmInline @Serializable
value class StringValue(val value: String): Element

object MySerializer : JsonContentPolymorphicSerializer<Element>(
    Element::class
) {
    override fun selectDeserializer(element: JsonElement) =
        when (element) {
            is JsonObject -> MyClass.serializer()
            else -> StringValue.serializer()
        }
}

data class A(
     val data: List<Element>
)
g
And it works right? I mean if you add @Serializable on top of A:
Copy code
@Serializable
    data class A(
        val data: List<Element>,
    )
    val json = Json {}

    @Test
    fun foo() {
        testSerialization(A(listOf(MyClass("foo"), StringValue("bar"))))
        testSerialization(A(listOf(MyClass("foo"), MyClass("bar"))))
        testSerialization(A(listOf(StringValue("foo"), StringValue("bar"))))
    }

    fun testSerialization(a: A) {
        val encoded = json.encodeToString(a)
        println(encoded)
        val decoded = json.decodeFromString<A>(encoded)
        println(decoded)
        assertEquals(a, decoded)
    }

// OUTPUT :
{"data":[{"uuid":"foo"},"bar"]}
A(data=[MyClass(uuid=foo), StringValue(value=bar)])
{"data":[{"uuid":"foo"},{"uuid":"bar"}]}
A(data=[MyClass(uuid=foo), MyClass(uuid=bar)])
{"data":["foo","bar"]}
A(data=[StringValue(value=foo), StringValue(value=bar)])
NB: it allows to mix MyClass & StringValue, is it fine for you?
o
Yes it worked for me. I've added @Serializable on top of A.
👍 1
Is it possible to do it without inline value class?
Hi Again @glureau. Sorry to interrupt again 🙈 Maybe you can give another hand.
g
I don't think so, Kotlin doesn't support union types afaik (so you cant say String&MyClass)
o
Yeah sure. I hoped there's a way using the serialization library. Custom serializer for string perhaps