I am trying to consume and emit JSON which contains a polymorphic list of items. The problem is: the items contain type key with integer values (not strings). The API endpoint produces and expects JSON similar to this:
{
"startTime": "2022-07-27T13:32:57.379Z",
"items": [
{
"type": 0,
"results": "string",
"restBetweenRounds": "string"
},
{
"type": 1,
"results": "string",
"setCount": 0
},
{
"type": 2,
"items": [
{
"type": 0,
"results": "string",
"restBetweenRounds": "string"
},
{
"type": 1,
"results": "string",
"setCount": 0
}
],
"results": "string"
}
],
"status": 0,
"clientId": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}
As described in the article on polymorphism, I created an hierarchy of classes. I also try to convert type value before deserialization.
object MyTransformingDeserializer : JsonTransformingSerializer<BaseItem>(PolymorphicSerializer(BaseItem::class)) {
override fun transformDeserialize(element: JsonElement): JsonElement {
val type = element.jsonObject["type"]!!
val newType = JsonPrimitive(value = type.toString())
return JsonObject(element.jsonObject.toMutableMap().also { it["type"] = newType })
}
}
@Serializable(with = MyTransformingDeserializer::class)
sealed class BaseItem {
abstract val type: String
}
@Serializable
@SerialName("0")
class ItemType0(
override val type: String,
// ...
) : BaseItem()
@Serializable
@SerialName("1")
class ItemType1(
override val type: String,
// ...
) : BaseItem()
@Serializable
@SerialName("2")
class ItemType2(
override val type: String,
// ...
) : BaseItem()
But all I get is this error:
kotlinx.serialization.json.internal.JsonDecodingException: Polymorphic serializer was not found for class discriminator '0'
Given that I can not change the format of the JSON, what can be done to successfully serialize/desereialize it?
Handling polymorphism in Kotlinx Serialization is difficult, especially when you don't have control over the format of the source. But KxS does give a lot of low-level tools to manually handle almost anything.
You were close in choosing
JsonTransformingSerializer! It seems that it doesn't transform the JSON before KxS selects a serializer. Because discriminators can only be strings, deserialization fails.JsonContentPolymorphicSerializerInstead of
JsonTransformingSerializer, you can useJsonContentPolymorphicSerializer.Kotlinx Serialization will first deserialize the JSON to a
JsonObject. It will then provide that object to the serializer forBaseItem, and you can parse and select the correct subclass.Including
typeSince this is manually performing polymorphic discrimination, there's no need to include
typein your classes.However you might like to include it, for completeness, and so it's included when serializing. For that, you must use
@EncodeDefaultComplete example
Bringing it all together, here's a complete example.
This prints
and
which matches the input!
Versions