Come usare i generici TypeToken + con Gson in Kotlin


108

Non riesco a ottenere un elenco di tipo generico da una classe personalizzata (Turni):

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

ha detto:

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

Risposte:


236

Crea questo divertimento in linea:

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

e poi puoi chiamarlo in questo modo:

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

Alternative precedenti:

ALTERNATIVA 1:

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

Devi inserire object :e il tipo specificofromJson<List<Turns>>


ALTERNATIVA 2:

Come menzionato da @cypressious può essere ottenuto anche in questo modo:

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

usare come:

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

4
Puoi anche creare un metodo di supporto che lo faccia per te:inline fun <reified T> genericType() = object: TypeToken<T>() {}.type
Kirill Rakhman

1
o addirittura estende Gson per avere un nuovo sovraccarico di fromJson che fa questo. Kotlin è progettato per estendersi, quindi estendi Gson per renderlo più bello e nascondere TypeTokens.
Jayson Minard,

Ho apportato una modifica suggerita che rende la risposta più completa e formale poiché è probabile che questa risposta venga vista da molti che usano Gson. Ho aggiunto spiegazioni nella risposta e collegamenti ai riferimenti di Kotlin per argomenti utilizzati per risolvere il problema ... quindi le persone non devono leggere tutte le altre risposte o commenti che sono stati inseriti in questo. Se accetti la modifica, posso rimuovere la mia risposta di seguito.
Jayson Minard,

Modifica rifiutata, vedi la mia risposta di seguito per una versione completa che combina tutte le risposte e i commenti in una risposta coerente. Hai accettato la tua risposta ma non è completa.
Jayson Minard

rimuovendo kotlin warning: inline fun <reified T> genericType (): Type? = oggetto: TypeToken <T> () {} .type
Juan Mendez

28

Questo risolve il problema:

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

La prima riga crea un'espressione oggetto che discende da TypeTokene quindi ottiene il Java Typeda quella. Quindi il Gson().fromJsonmetodo richiede il tipo specificato per il risultato della funzione (che dovrebbe corrispondere a quello TypeTokencreato). Due versioni di questo lavoro, come sopra o:

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

Per semplificare la creazione di TypeTokenè possibile creare una funzione di supporto, che deve essere in linea in modo che possa utilizzare parametri di tipo reificati :

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

Che può quindi essere utilizzato in uno di questi modi:

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

E l'intero processo può essere avvolto in una funzione di estensione per l' Gsonistanza:

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

In modo che tu possa semplicemente chiamare Gson e non preoccuparti TypeTokenaffatto:

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

Qui Kotlin sta usando l'inferenza del tipo da un lato dell'assegnazione o dall'altro, e generici reificati per una funzione inline per passare attraverso il tipo completo (senza cancellazione), e lo usa per costruire TypeTokene anche fare la chiamata a Gson


1
Ciao @Jayson, non sono riuscito a farlo funzionare in questo modo divertente in linea in Android Studio. Sembra essere OK ma non viene riconosciuto quando lo faccioGson().fromJson<kotlin.List<Turns>>(pref.turns)
Juan Saravia,

@juancho puoi dirmi cosa significa "non riconosciuto"? Un errore del compilatore? Hai il metodo di estensione importato e disponibile dall'alto?
Jayson Minard

2
Copio e incollo il tuo codice in Android Studio e ho importato il divertimento nella mia classe Kotlin. Ho provato a fare quello che hai detto ma per qualche motivo il compilatore mi ha detto che questo divertimento non esiste. Sto già utilizzando altre funzioni di estensione ma non so quale sia il tuo suggerimento non funziona. Quale versione di AS e Kotlin stai usando? solo per riprovare.
Juan Saravia

Questo non è correlato direttamente allo studio Android, Kotlin è lo stesso dentro o fuori di esso. Stai creando un'istanza Gson()o semplicemente Gsoncome se fosse statica? Hai bisogno del primo, un'istanza.
Jayson Minard

21

Un'altra opzione (non sono sicuro che sembri più elegante delle altre) potrebbe essere una chiamata come questa:

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

Quindi stai usando la linea java Array di classe uno invece di "puro Kotlin".


2
Poiché TypeToken non funziona su tutti i telefoni affidabili, questa è la soluzione migliore per me. Un semplice rivestimento con puro kotlin.
LuckyMalaka

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

È il mio modo per analizzare l'array di dati in kotlin.


Questa dovrebbe essere la risposta accettata, breve e semplice.
Umar Ata

6

Ho usato qualcosa di simile per convertire Ta string& Stringtornare a Tutilizzare Gson. Non esattamente quello che stai cercando, ma per ogni evenienza.

Dichiarazione di estensione

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)

uso

// 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
        }
    }

5

Anche questo funziona ed è più semplice

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

Il tipo restituito deve essere nullable. In caso contrario, il codice Java nella libreria Gson può restituire null, ma Kotlin presume che il tipo non sia nullable. Di conseguenza, ottieni NPE a Kotlin.
fdermishin

1

Kotlin generic reified functiondi Gson deserializza per ArrayList<T>utilizzare questo codice

 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
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.