153

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'
Juan Saravia
  • 7,661
  • 6
  • 29
  • 41

7 Answers7

321

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>>()
Vlad
  • 7,997
  • 3
  • 56
  • 43
Juan Saravia
  • 7,661
  • 6
  • 29
  • 41
  • 4
    You can also create a helper method that does that for you: `inline fun genericType() = object: TypeToken() {}.type` – Kirill Rakhman Oct 28 '15 at 10:18
  • 1
    or even extends Gson to have a new overload of fromJson that does this. Kotlin is designed to extend, so extend Gson to make it nicer and hide TypeTokens. – Jayson Minard Oct 29 '15 at 16:36
  • I made a suggested edit that makes the answer more complete and formal since this answer is likely to be seen by many who use Gson. I added explanations in the answer, and links to Kotlin references for topics used to solve the problem ... so people do not have to read all the other answers or comments that went into this. If you accept the edit I can remove my answer below. – Jayson Minard Dec 27 '15 at 11:28
  • Edit rejected, see my answer below for a full version that combines all answers and comments into one coherent answer. You accepted your own answer but it isn't complete. – Jayson Minard Dec 28 '15 at 00:18
  • removing kotlin warning: inline fun genericType() : Type? = object: TypeToken() {}.type – Juan Mendez Sep 28 '18 at 18:21
  • kotlin is way more concise and powerful language than java. – Sadda Hussain Sep 06 '19 at 07:55
  • wow and they say Kotlin is more concise and easier... wow this is horrible. but thank you! – Radu Apr 29 '20 at 08:09
  • this doesnt work apparently TypeToken throws a compile error saying cannot access init – nikoss Jul 24 '20 at 23:00
  • What is the explanation/reason for doing this in Kotlin (and Java from what it looks like)? – 3366784 Dec 16 '20 at 19:44
37

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

Jayson Minard
  • 84,842
  • 38
  • 184
  • 227
  • 1
    Hi @Jayson, I could't make it works this inline fun in Android Studio. Seems to be OK but it is not recognised when I do `Gson().fromJson>(pref.turns)` – Juan Saravia Oct 29 '15 at 17:53
  • @juancho can you tel me what "not recognized" means? A compiler error? You have the extension method imported and available from above? – Jayson Minard Dec 27 '15 at 11:14
  • 2
    I copy and paste your code in Android Studio and imported the fun in my kotlin class. I tried to do what you said but for some reason the compiler tell me that this fun doesn't exist. I'm already using other extention functions but I don't know what your suggestion doesn't work. Which version of AS and Kotlin are you using? just to try it again. – Juan Saravia Dec 28 '15 at 13:08
  • This isn't related to Android studio directly, Kotlin is the same inside or outside of that. Are you creating instance of `Gson()` or just `Gson` as-if it is static? You need the first, an instance. – Jayson Minard Jan 04 '16 at 21:46
31

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

Tobias Reich
  • 4,952
  • 3
  • 47
  • 90
  • 3
    Since TypeToken do not work on every phone reliable, this is the best solution for me. An easy one liner with pure kotlin. – 0xPixelfrost Jul 23 '18 at 18:23
14
val obj: MutableList<SaleItemResponse> = Gson().fromJson(messageAfterDecrypt,
    object : TypeToken<List<SaleItemResponse>>() {}.type)

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

Toàn Mỹ
  • 141
  • 1
  • 2
7

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
        }
    }
harsh_v
  • 3,193
  • 3
  • 34
  • 53
7

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
}
Moslem Shahsavan
  • 1,211
  • 17
  • 19
6

This works as well, and is simpler

    inline fun <reified T> Gson.fromJson(json: String) : T = 
         this.fromJson<T>(json, T::class.java)
Christopher Perry
  • 38,891
  • 43
  • 145
  • 187
  • 1
    The return type must be nullable. Otherwise Java code in Gson library can return null, but Kotlin assumes the type is non-nullable. As a result, you get NPE in Kotlin. – fdermishin Dec 29 '17 at 09:57
  • not working for me . its returning list of `LinkedTreeMap` , not list for required data class – Abu Yousuf Jan 23 '22 at 15:23