I get the following JSON from an API to describe an object with different entries.
{
"entries": [
"Simple Text",
{
"type": "list",
"items": [
"First Item",
"Second Item",
"Spectral wings appear on your back, giving you a flying speed of 40 feet."
]
},
{
"type": "entries",
"name": "Sub-Entry",
"entries": [
"New text but could also be the same as above"
]
}
]
}
In kotlin I would like to have a Entry class with a different sub-classes for one each type, so e.g. EntryText, EntryList, ...
How can i decode this into the classes since the different elements of the JsonArray are of a different type?
The problem is the a polymorphic approach would not work since the "Simple Text"-entry has no type.
You could use a Json transformation to alter your received JSON before normal serialization takes place.
This class:
class EntryPreSerializer : JsonTransformingSerializer<Entry>(Entry.serializer()) {
override fun transformDeserialize(element: JsonElement): JsonElement {
return if (element is JsonPrimitive)
buildJsonObject {
put("type", JsonPrimitive("StringEntry"))
put("text", element)
}
else element
}
}
when applied to a serialized Entry
object (our polymorphic class) will add the discriminator we need for polymorphic deserialization when it finds a JSON primitive amongst the entries
.
To complete the example, here is the rest of the deserialization filled in, including showing how we apply the transformation:
@Serializable
data class EntryWrapper(val entries: List<@Serializable(with = EntryPreSerializer::class) Entry>)
@Serializable
sealed class Entry
@Serializable
data class StringEntry(val text: String) : Entry()
@Serializable @SerialName("list")
data class ListEntry(val items: List<String>) : Entry()
@Serializable @SerialName("entries")
data class EntriesEntry(val name: String, val entries: List<String>) : Entry()
Then we can test:
fun main() {
val testJson = """
{
"entries": [
"Simple Text",
{
"type": "list",
"items": [
"First Item",
"Second Item",
"Spectral wings appear on your back, giving you a flying speed of 40 feet."
]
},
{
"type": "entries",
"name": "Sub-Entry",
"entries": [
"New text but could also be the same as above"
]
}
]
}
""".trimIndent()
println(Json.decodeFromString<EntryWrapper>(testJson))
}
Which prints:
EntryWrapper(entries=[StringEntry(text=Simple Text), ListEntry(items=[First Item, Second Item, Spectral wings appear on your back, giving you a flying speed of 40 feet.]), EntriesEntry(name=Sub-Entry, entries=[New text but could also be the same as above])])