androidkotlinserializationdeserializationheterogeneous-array

Kotlin Deserialization - JSON Array to multiple different objects


I'm using the 1.0.0 version of kotlin serialization but I'm stuck when I try to deserialize a "flexible" array.

From the Backend API that I don't control I get back an JSON Array that holds different types of objects. How would you deserialize them using kotlin serialization?

Example

This is the API's response

[
  {
    "id": "test",
    "person": "person",
    "lastTime": "lastTime",
    "expert": "pro"
  },
  {
    "id": "test",
    "person": "person",
    "period": "period",
    "value": 1
  }
]
@Serializable
sealed class Base {
  @SerialName("id")
  abstract val id: String
  @SerialName("person")
  abstract val person: String
}

@Serializable
data class ObjectA (
 @SerialName("id") override val id: String,
 @SerialName("title") override val title: String,
 @SerialName("lastTime") val lastTime: String,
 @SerialName("expert") val expert: String
) : Base()

@Serializable
data class ObjectB (
 @SerialName("id") override val id: String,
 @SerialName("title") override val title: String,
 @SerialName("period") val period: String,
 @SerialName("value") val value: Int
) : Base()


Performing the following code result in an error

println(Json.decodeFromString<List<Base>>(json))

error Polymorphic serializer was not found for class discriminator


Solution

  • @cactustictacs solution came very close. He said that "By default sealed classes are handled by adding a type field to the JSON"

    But because I didn't had a type property I needed a other field that decides which subclass it should be.

    In Kotlin Serializer you can do that by

     val format = Json {
        classDiscriminator = "PROPERTY_THAT_DEFINES_THE_SUBCLASS"
     }
     val contentType = MediaType.get("application/json")
     val retrofit = Retrofit.Builder()
                    .baseUrl(baseUrl)
                    .client(client)
                    .addConverterFactory(format.asConverterFactory(contentType))
                    .build()
    

    where in classDiscriminator you can enter the property that you want. Hope this helps other people in the future.