How to make a generic serialized name in retrofit?

Viewed 358

Most of my API calls return something like this:

{
  "message": str,
  "status": str,
  "different_name_depending_on_endpoint":{
    // bunch of stuff
  },
}

How can I make the name that goes in the "different_name_depending_on_endpoint" key generic? Like so :

data class ReturnAPI<T>(
    @SerializedName("message")
    val message: String,
    @SerializedName("status")
    val status: String,
    @SerializedName("how do I make this generic")
    val data: T
)

Otherwise for every endpoint I have to create a separate data class for the api return, and a separate data class for the data field, which is impractical. There must be a way to make it generic, right? Or to tell Retrofit to try to fill the data field with whatever else is in the JSON file that isn't message or status.

1 Answers

Or to tell Retrofit to try to fill the data field with whatever else is in the JSON file that isn't message or status

Yes, you can write a custom deserializer for ReturnAPI type:

@JsonAdapter(ReturnApiTypeAdapter::class)
data class ReturnAPI<T>(
    val message: String,
    val status: String,
    val data: T
)

class ReturnApiTypeAdapter : JsonDeserializer<ReturnAPI<*>> {

    @Throws(JsonParseException::class)
    override fun deserialize(
        json: JsonElement,
        typeOfT: Type,
        context: JsonDeserializationContext
    ): ReturnAPI<*>? {
        if (json.isJsonNull) {
            return null
        }
        val jsonObject = json.asJsonObject
        // get actual type of Data
        val typeOfData = (typeOfT as ParameterizedType)
            .actualTypeArguments[0]

        var message: String? = null
        var status: String? = null
        var data: Any? = null

        for (key in jsonObject.keySet()) {
            when (key) {
                "message" -> message = jsonObject.getAsJsonPrimitive(key).asString
                "status" -> status = jsonObject.getAsJsonPrimitive(key).asString
                else -> data = context.deserialize(jsonObject.get(key), typeOfData)
            }
        }
        // optional checks
        check(message != null && status != null) {
            "Failed parsing"
        }
        return ReturnAPI(message, status, data)
    }
}
Related