0

I wrote a Java App where data was retrieved from an API in JSON. Depending on the endpoint the actual data could be very divers. So I made a function to convert the data to the required class for every possible class. This gives me multiple functions which only differ in the classtype to convert to. Following are two examples:

public List<ZermeloBranch> processBranches(ZermeloApiResponseObject responseObject) {
        List<ZermeloBranch> branches = new ArrayList<>();
        List<LinkedTreeMap> branchMaps = responseObject.getData();
        Gson gson = new Gson();
        for (LinkedTreeMap treeMap : branchMaps) {
            branches.add(gson.fromJson(gson.toJsonTree(treeMap).toString(), ZermeloBranch.class));
        }
        return branches;
    }

public List<ZermeloAnnouncement> processAnnouncements(ZermeloApiResponseObject responseObject) {
        List<ZermeloAnnouncement> announcements = new ArrayList<ZermeloAnnouncement>();
        List<LinkedTreeMap> announcementMaps = responseObject.getData();
        Gson gson = new Gson();
        for (LinkedTreeMap treeMap : announcementMaps) {
            announcements.add(gson.fromJson(gson.toJsonTree(treeMap).toString(), ZermeloAnnouncement.class));
        }
        return announcements;
    }

Now I am rewriting this app to Kotlin and I summise it should be possible to write one function to process the data passing the class to decode to as a parameter. So I made both ZermeloBranch and ZermeloAnnouncement inherit from ZermeloDataObject. I would like to write one function like this:

fun processDataToList(data:JSONArray, convertToClass:KClass<out ZermeloDataObject>):List<KClass<out ZermeloDataObject>>{
    val returnList:ArrayList<KClass<out ZermeloDataObject>> = arrayListOf()
    val gson = Gson()
    for (item in 0 until data.length()){
        returnList.add(gson.fromJson(item, convertToClass))
    }
    return returnList
}

and call it with processDataToList(data, ZermeloAnnouncements::class) and get a List<ZermeloAnnoucement> returned of call it with processDataToList(data, ZermeloBranch::class) and get a List<ZermeloBranch> returned. Alas, the compiler gives me an error on gson.fromJson stating "None of the following functions can be called with the arguments supplied" and then it lists all possible functions.

Is it possible to use one function as I propose, and if so, what am I doing wrong?

1 Answer 1

1

You could use TypeToken provided by Gson to correctly handle the return type. Besides this you are passing index instead of data.

val type = object: TypeToken<KClass<out ZermeloDataObject>>() {}.type

fun processDataToList(data:JSONArray, convertToClass:KClass<out ZermeloDataObject>):ArrayList<KClass<out ZermeloDataObject>>{
    val returnList:ArrayList<KClass<out ZermeloDataObject>> = arrayListOf()
    val type = object: TypeToken<KClass<out ZermeloDataObject>>() {}.type
    val gson = Gson()

    for (item in 0 until data.length()){
        returnList.add(gson.fromJson(data[item].toString(), type))
    }

    return returnList
}

Instead of this you could do the whole thing in a single line of code. Check below:

fun processDataToList(data:JSONArray, convertToClass:KClass<out ZermeloDataObject>):List<KClass<out ZermeloDataObject>>{
    return Gson().fromJson(data.toString(), object: TypeToken<List<KClass<out ZermeloDataObject>>>() {}.type)
}

Beside this, you can also use below style which is more generic:

fun <T> processDataToList(data:JSONArray): List<T> {
    val type = object: TypeToken<T>() {}.type
    val returnList: ArrayList<T> = arrayListOf()
    val gson = Gson()

    for (item in 0 until data.length()) {
        returnList.add(gson.fromJson(json[item].toString(), type))
    }

    return returnList
}

Or Simply

fun <T> processDataToList(data:JSONArray):List<T>{
    return Gson().fromJson(data.toString(), object: TypeToken<List<T>>() {}.type)
}
Sign up to request clarification or add additional context in comments.

5 Comments

Thank you. I do have a question. The parameter convertToClass is not used. How is the type set to the class I want to use?
convertToClass is a generic type which will be inferred from the call when you use the method. Hence you don’t need to explicitly pass type parameter to it
The added solutions are what I needed. I definitely need more experience with generics. Thank you
Didn't get you. Can you explain more?
I should be able to come up with your last solution myself, so I need to study generics in depth. I used your solution. Thanks

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.