Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kotlin Gson Deserializing

I am receiving a JSON data model that has a map wrapper Table. I'm trying to use generics to pass in the type that is beyond the wrapper but it's not translating well at runtime. Here's an example of my JSON file:

{
"Table": [
 {
   "paymentmethod_id": 1,
   "paymentmethod_description": "Cash",
   "paymentmethod_code": "Cash",
   "paymentmethod_is_ach_onfile": false,
   "paymentmethod_is_element": false,
   "paymentmethod_is_reward": false,
   "paymentmethod_is_openedgeswipe": false,
   "paymentmethod_update_user_id": 1,
   "paymentmethod_insert_user_id": 1,
   "paymentmethod_insertdate": "2014-10-07 14:53:16",
   "paymentmethod_deleted": false,
   "paymentmethod_is_mobile_visible": true
   }
  ]
}

The wrapper class I'm using is called Table.

data class Table<T>(
    @SerializedName("Table") val models : Array<T>
)

The actual model class is PaymentMethod.

data class PaymentMethod(
    @SerializedName("paymentmethod_id") val idNumber : Int = -1
)

I have created a generic data manager class that takes < T > type. I think use subclasses of the data manager to localize the input and results (such as declaring the model class PaymentMethod.

open class NXDataManager<T>(manager: NXNetworkManager? = null, rpc : String?, parameters: List<Pair<String, String>>? = null, method : String = "get")
{
   ...


open fun sendRequest(completionHandler: (models:Array<T>) -> Unit, errorHandler: (error:FuelError) -> Unit) {

    val request = NXNetworkRequest(rpc, parameters, method)

    request.send(manager, completionHandler = { s: String ->

        val table: Table<T> = Gson().fromJson(s)

        completionHandler(table.models)

    }, errorHandler = errorHandler)
}

inline fun <reified T> Gson.fromJson(json: String) = this.fromJson<T>(json, object: TypeToken<T>() {}.type)

}

My subclassed data manager specifies the model to parse into.

final public class PaymentMethodsDataManager : NXDataManager<PaymentMethod>
{
   constructor () : super("genGetPaymentMethods")

}

When I run the code as:

val table: Table<T> = Gson().fromJson(s)

I get an error message java.lang.ClassCastException: java.lang.Object[] cannot be cast to Networking.PaymentMethod[]. However, when I pass in an explicit type it works as expected--parsing the array into PaymentMethod models:

val table: Table<PaymentMethod> = Gson().fromJson(s)

Any ideas of how I can still use the generic type T?

like image 387
Hobbes the Tige Avatar asked Dec 22 '17 16:12

Hobbes the Tige


2 Answers

Data Class :

data class Table<T>(
    @SerializedName("Table") val models : Array<T>
)

to JSON:

val gson = Gson()
val json = gson.toJson(table)

from JSON:

val json = getJson()
val table = gson.fromJson(json, Table::class.java)
like image 163
chandrakant sharma Avatar answered Sep 17 '22 18:09

chandrakant sharma


Method fromJson is generic, so when you call it for Table<T> variable it creates Array<Any> as most suitable. You need to notice that PaymentMethod class extends T generic, but I don't know is it even possible. If you find out how to make it, use something like following:

val table: Table<T> = Gson().fromJson<Table<PaymentMethod>>(s)

In your case I'm using gson adapters. Following function creates object with specified type parameter:

fun getObjectFromString(type: Type, string: String) =
    Gson().getAdapter(TypeToken.get(type)).fromJson(string)

To use it write something following:

val table: Table<T> = getObjectFromString(Table<PaymentMethod>::class.java, s) as Table<PaymentMethod>

Update

To avoid spare class cast you can use reified generic function:

inline fun <reified T> getObjectFromString(string: String): T =
            getGsonConverter().getAdapter(TypeToken.get(T::class.java)).fromJson(string)!!

In that case using would be easier:

val table: Table<T> = getObjectFromString<Table<PaymentMethod>>(s)

I used first solution in cases where I don't know what type the object would be - I've got only Type variable with information about that object.

like image 42
Ircover Avatar answered Sep 18 '22 18:09

Ircover