How to use TypeToken + generics with Gson in Kotlin

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>>()

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".


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