0

I'm building a multi-platform JSON library as a prototype to get a feel for how to structure things.

Currently i have this extension function to convert from a JSON Array to a List of Objects.

inline fun <reified T : Any> String.fromJsonList() : List<T> = 
    provider.fromJsonList(this, T::class)

Implementing this interface for Jackson is rather straight forward:

class JvaasJackson : JsonProvider {

    val mapper = ObjectMapper();

    ...

    override fun <T : Any> fromJsonList(input: String, type: KClass<T>): List<T> {
        return mapper.readValue(input, mapper.typeFactory.constructCollectionType(List::class.java, type.java)) ?: listOf()
    }

}

Now i'm trying to do the exact same thing with Gson:

class JvaasGson : JsonProvider {

    val gsonNormal = com.google.gson.Gson()
    val gsonPretty = com.google.gson.GsonBuilder().setPrettyPrinting().create()

    ...

    override fun <T : Any> fromJsonList(input: String, type: KClass<T>): List<T> {

        TODO("fix this")

    }

}

Looking at other answers which are in Java, this seems to be the pattern to follow:

Type listType = new TypeToken<List<MyModel>>(){}.getType();
List<MyModel> myModelList = gson.fromJson(jsonArray, listType);

My Kotlin equivalent fails:

val listType = object : TypeToken<List<T>>() {}.type
val myModelList = gsonNormal.fromJson<List<T>>(input, listType) ?: listOf()

with:

java.lang.ClassCastException: java.util.ArrayList cannot be cast to [Ljava.lang.Object;

This:

val listType = object : TypeToken<ArrayList<T>>() {}.type
val myModelList = gsonNormal.fromJson(input, listType) as ArrayList<T>

and this:

val listType = object : TypeToken<ArrayList<T>>() {}.type
val myModelList = gsonNormal.fromJson<ArrayList<T>>(input, listType)

fails with:

java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to io.jvaas.integration.gson.SampleClass

where SampleClass is the type of T

If i remove the types and print the result

    val listType = object : TypeToken<ArrayList<T>>() {}.type
    val list= gsonNormal.fromJson<Any>(input, listType)

    println(list)
    println(list::class)
    println(list::class.java)

i see the correct data:

[{blah1=text, blah2=123.0, blah3=567.0}, {blah1=text, blah2=123.0, blah3=567.0}]

class java.util.ArrayList

class java.util.ArrayList

Printing out the first item of that ArrayList, i see however that it's not of type SampleClass, but rather LinkedTreeMap

println((list as List<T>).first()::class)

class com.google.gson.internal.LinkedTreeMap

Converting each item in that list to String, then converting each individual blob of JSON to objects seems to work, but looks very hacky

override fun <T : Any> fromJsonList(input: String, type: KClass<T>): List<T> {

    val listType = object : TypeToken<ArrayList<*>>() {}.type
    val list= gsonNormal.fromJson<List<Any>>(input, listType) ?: listOf<Any>()

    val results = mutableListOf<T>()
    list.forEach { item ->
        results.add(gsonNormal.fromJson(item.toString(), type.java))
    }

    return results

}

Is there a better way of doing this? Or does Gson always convert more complex JSON data into TreeMaps and LinkedTreeMaps?

2
  • A side note, Kotlin does have a multi-platform serialization library for JSON as part of the system already: github.com/Kotlin/kotlinx.serialization Commented Nov 25, 2018 at 13:43
  • Nice! Will use that for Non-JVM projects or projects that doesn't already have Gson / Jackson onboard. Commented Nov 25, 2018 at 14:06

0

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.