Juan Saravia
Juan Saravia

Reputation: 7711

How to use TypeToken + generics with Gson in Kotlin

I'm unable to get a List of generic type from a custom class (Turns):

val turnsType = TypeToken<List<Turns>>() {}.type
val turns = Gson().fromJson(pref.turns, turnsType)

it said:

cannot access '<init>' it is 'public /*package*/' in 'TypeToken'

Upvotes: 153

Views: 74760

Answers (7)

Tobias Reich
Tobias Reich

Reputation: 5160

Another option (not sure it looks more elegant than the others) might be a call like this:

turns = Gson().fromJson(stringObject, Array<Turns>::class.java).toMutableList()

So you are using the java Array class one liner instead of "pure Kotlin".

Upvotes: 31

Moslem Shahsavan
Moslem Shahsavan

Reputation: 1337

Kotlin generic reified function of Gson deserialize to ArrayList<T> use this code

 inline fun <reified T> get( ... ): ArrayList<T>{
    
    val str = "[{},{}]"
    
    val type = TypeToken.getParameterized(ArrayList::class.java, T::class.java).type
    
    val t = Gson().fromJson<ArrayList<T>>(str, type)
    

    return t
}

Upvotes: 7

Juan Saravia
Juan Saravia

Reputation: 7711

Create this inline fun:

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

and then you can call it in this way:

val turns = Gson().fromJson<Turns>(pref.turns)
// or
val turns: Turns = Gson().fromJson(pref.turns)

Previous Alternatives:

ALTERNATIVE 1:

val turnsType = object : TypeToken<List<Turns>>() {}.type
val turns = Gson().fromJson<List<Turns>>(pref.turns, turnsType)

You have to put object : and the specific type in fromJson<List<Turns>>


ALTERNATIVE 2:

As @cypressious mention it can be achieved also in this way:

inline fun <reified T> genericType() = object: TypeToken<T>() {}.type

use as:

val turnsType = genericType<List<Turns>>()

Upvotes: 322

harsh_v
harsh_v

Reputation: 3269

I used something like this to convert T to string & String back to T using Gson. Not exactly what you are looking for but just in case.

Declaring extension

inline fun <reified T : Any> T.json(): String = Gson().toJson(this, T::class.java)
inline fun <reified T : Any> String.fromJson(): T = Gson().fromJson(this,T::class.java)

Usage

// Passing an object to new Fragment
companion object {    
        private const val ARG_SHOP = "arg-shop"

        @JvmStatic
        fun newInstance(shop: Shop) =
                ShopInfoFragment().apply {
                    arguments = Bundle().apply {
                        putString(ARG_SHOP, shop.json())
                    }
                }
    }

// Parsing the passed argument
private lateinit var shop: Shop

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arguments?.let {
            shop = it.getString(ARG_SHOP).fromJson() ?: return
        }
    }

Upvotes: 8

To&#224;n Mỹ
To&#224;n Mỹ

Reputation: 141

val obj: MutableList<SaleItemResponse> = Gson().fromJson(messageAfterDecrypt,
    object : TypeToken<List<SaleItemResponse>>() {}.type)

It's my way to parsing data array in kotlin.

Upvotes: 14

Christopher Perry
Christopher Perry

Reputation: 39235

This works as well, and is simpler

    inline fun <reified T> Gson.fromJson(json: String) : T = 
         this.fromJson<T>(json, T::class.java)

Upvotes: 6

Jayson Minard
Jayson Minard

Reputation: 85996

This solves the problem:

val turnsType = object : TypeToken<List<Turns>>() {}.type
val turns = Gson().fromJson<List<Turns>>(pref.turns, turnsType)

The first line creates an object expression that descends from TypeToken and then gets the Java Type from that. Then the Gson().fromJson method either needs the type specified for the result of the function (which should match the TypeToken created). Two versions of this work, as above or:

val turns: List<Turns> = Gson().fromJson(pref.turns, turnsType)

To make it easier to create the TypeToken you can create a helper function, which is required to be inline so that it can use reified type parameters:

inline fun <reified T> genericType() = object: TypeToken<T>() {}.type

Which can then be used in either of these ways:

val turnsType = genericType<List<Turns>>()
// or
val turnsType: List<Turns> = genericType()

And the whole process can be wrapped into an extension function for the Gson instance:

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

So that you can just call Gson and not worry about the TypeToken at all:

val turns = Gson().fromJson<Turns>(pref.turns)
// or
val turns: Turns = Gson().fromJson(pref.turns)

Here Kotlin is using type inference from one side of the assignment or the other, and reified generics for an inline function to pass through the full type (without erasure), and using that to construct a TypeToken and also make the call to Gson

Upvotes: 37

Related Questions