0

How to use Gson to do the parse if the the class type is a generic type 'T'?

Having a class with generic type, and in its function which would parse the json string into whatever that generic type 'T' is at the runtime. but got

java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to com.example.model.content

this does not work for the generic is 'T':

        val dataType = object : TypeToken<T>() {}.type
        val data = Gson().fromJson<T>(jsonStr, dataType)

this is how the call is called with the 'solid' class type Content for the Handler<Content> passed in where the generic 'T' (IHandler<T>) is expected

FetchRemoteDataCommand("https", “testcontent.com", "v2/content”,
    params,
    Handler<Content>).execute()

this is the class takes a generic type: IHandler<T>, expecting at runtime the 'T' should be the 'solid' type, i.e. it should be the Content type by the calling

class FetchRemoteDataCommand<T>(scheme: String, authority: String, path: String,
                         params: HashMap<String, String>,
                         dataReadyCb: IHandler<T>) {


val mBaseUrl = "$scheme://$authority/"
val mPath = path
val mParams = params
var mDataReadyListener = dataReadyCb

override fun dispose() {……}

override fun execute() {
    getRemoteData(object : Observer<ResponseBody> {
        override fun onNext(responseBody: ResponseBody) {

            val dataType = object : TypeToken<T>() {}.type
            val jsonStr = responseBody.string()
            val data = Gson().fromJson<T>(jsonStr, dataType)  ***//<=== throws***

            ///
            mDataReadyListener(data)  //<== suppose to pass back the pared data back
            ……
        }
        …………
    })
}

private fun getRemoteData(observer: Observer<ResponseBody>) : Unit {

    val service: IRemoteRepositoryService = ServiceFactory.createRetrofitService(
                          IRemoteRepositoryService::class.java, mBaseUrl)

    service.getRemoteData(mPath, mParams) 
           .subscribe(observer)

    }
 }
lannyf
  • 9,865
  • 12
  • 70
  • 152

1 Answers1

0

got idea from Henning and (also Lin Yu Cheng has a solution for getting it at runtime) in the get-generic-type-of-class-at-runtime so passing in the class type instead of get the type at runtime.

class FetchRemoteDataCommand<T>(scheme: String, authority: String, path: String,
                     params: HashMap<String, String>,
                     dataReadyCb: IHandler<T>, handlerClassType: Class<T>) {


val mBaseUrl = "$scheme://$authority/"
val mPath = path
val mParams = params
var mDataReadyListener = dataReadyCb
private val clazz: Class<T> = handlerClassType //findTypeArguments(this.javaClass)  <== some time return null???

private fun findTypeArguments(t: Type) : Class<T> {
    return if (t is ParameterizedType) {
        val typeArgs = t.actualTypeArguments
        typeArgs[0] as Class<T>
    } else {
        val c = t as Class<*>
        findTypeArguments(c.genericSuperclass)
    }
}

fun getClassType(): Class<T> {
    return clazz
}


override fun execute() {
    getRemoteData(object : Observer<ResponseBody> {
        override fun onNext(responseBody: ResponseBody) {

            //val dataType = object : TypeToken<T>() {}.type
            //val jsonStr = responseBody.string()
            //val data = Gson().fromJson<T>(jsonStr, dataType)  ***//<=== throws***

/// test generic type with the passed in class type instead of reflecting at runtime
            val responseRecieved = responseBody.string()
            val data = Gson().fromJson(responseRecieved, getClassType())

            ///
            mDataReadyListener(data)  //<== suppose to pass back the pared data back
        ……
    }
    …………
    })
}
lannyf
  • 9,865
  • 12
  • 70
  • 152