0

I am needing our GSON parser to be able to handle an array vs single objects. I've got it working to where it recognizes any MutableList and falls into the Deserializer adapter below. However, I'm missing a piece where it's not liking the "T" type when actually doing the serialization into an object. I understand that it needs the type at this point, but is there something I'm missing to be able to infer this given what we have here?

class JsonArrayObjectAdapter<T>: JsonDeserializer<MutableList<T>> {
    override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?): MutableList<T> {
        val list = mutableListOf<T>()
        json?.let {
            context?.let {
                if (json.isJsonArray) {
                    for (element in json.asJsonArray) {
                        val obj: T = context.deserialize(element, T::class.java) // does not like this
                        list.add(obj)
                    }
                } else if (json.isJsonObject) {
                    val obj: T = context.deserialize(json, T!!::class.java) // does not like this
                    list.add(obj)
                }
            }
        }
        return list
    }
}

I get this compile error when trying to use T::class.java :

Cannot use 'T' as reified type parameter. Use a class instead.

Here's an example of a data class I'm using, for reference:

data class ExampleDataClass(
    @SerializedName("ExampleKey") val exampleVar: MutableList<ExampleSubDataClass>
)

And here is how I do the gson creation:

val listType = object : TypeToken<MutableList<ExampleDataSubClass>>() {}.getType()
val gson = GsonBuilder()
        .registerTypeAdapter(listType, JsonArrayObjectAdapter<ExampleDataSubClass>())
        .create()
result = gson.fromJson(jsonString, ExampleDataClass::class.java)
svguerin3
  • 2,433
  • 3
  • 29
  • 53

1 Answers1

1

In order to fix this, I had to just make multiple Adapters for each type of Object that can be an array or an object. It's unfortunate, since I had about 50 different types of those objects, so it's a lot of repeat code with calling "registerTypeAdapters" in the GsonBuilder.

To simplify a bit, I made a generic method to return an array based on a given JsonElement and generic T object:

fun <T> getJSONArray(json: JsonElement, type: Type, ctx: 
JsonDeserializationContext): ArrayList<T> {
    val list = ArrayList<T>()
    if (json.isJsonArray) {
        for (e in json.asJsonArray) {
            list.add(ctx.deserialize<Any>(e, type) as T)
        }
    } else if (json.isJsonObject) {
        list.add(ctx.deserialize<Any>(json, type) as T)
    } else {
        throw RuntimeException("Unexpected JSON type: " + json.javaClass)
    }
    return list
}

And an example of a definition of an Adapter utilizing the above method:

class JsonArrayObjectAdapterExampleDataClass: JsonDeserializer<ArrayList<ExampleDataClass>> {
    override fun deserialize(json: JsonElement, typeOfT: Type, ctx: JsonDeserializationContext): ArrayList<ExampleDataClass> {
        return getJSONArray(json, ExampleDataClass::class.java, ctx)
    }
}

It does not work to simply pass the typeOfT var to the getJSONArray method, as I had to explicitly send the specific type instead.

svguerin3
  • 2,433
  • 3
  • 29
  • 53
  • can you help me to my stuck https://stackoverflow.com/questions/68664774/handle-dynamic-response-sometimes-object-array-on-same-key-on-android-kotlin – Zainal Fahrudin Aug 05 '21 at 10:33