Comment utiliser les génériques TypeToken + avec Gson dans Kotlin


108

Je ne parviens pas à obtenir une liste de type générique à partir d'une classe personnalisée (tours):

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

Ça disait:

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

Réponses:


237

Créez ce plaisir en ligne:

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

et ensuite vous pouvez l'appeler de cette façon:

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

Alternatives précédentes:

ALTERNATIVE 1:

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

Vous devez mettre object :et le type spécifique dansfromJson<List<Turns>>


ALTERNATIVE 2:

Comme @cypressious le mentionne, cela peut également être réalisé de cette manière:

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

utilisé comme:

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

4
Vous pouvez également créer une méthode d'aide qui le fait pour vous:inline fun <reified T> genericType() = object: TypeToken<T>() {}.type
Kirill Rakhman

1
ou même étend Gson pour avoir une nouvelle surcharge de fromJson qui fait cela. Kotlin est conçu pour s'étendre, alors étendez Gson pour le rendre plus agréable et masquer les TypeTokens.
Jayson Minard

J'ai fait une modification suggérée qui rend la réponse plus complète et formelle, car cette réponse est susceptible d'être vue par de nombreux utilisateurs de Gson. J'ai ajouté des explications dans la réponse et des liens vers des références Kotlin pour les sujets utilisés pour résoudre le problème ... afin que les gens n'aient pas à lire toutes les autres réponses ou commentaires qui y sont liés. Si vous acceptez la modification, je peux supprimer ma réponse ci-dessous.
Jayson Minard

Modification rejetée, voir ma réponse ci-dessous pour une version complète qui combine toutes les réponses et commentaires en une seule réponse cohérente. Vous avez accepté votre propre réponse, mais elle n'est pas complète.
Jayson Minard

suppression de l'avertissement de kotlin: fun en ligne <reified T> genericType (): Type? = object: TypeToken <T> () {} .type
Juan Mendez

28

Cela résout le problème:

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

La première ligne crée une expression d'objet qui descend de TypeTokenpuis obtient le Java à Typepartir de cela. Ensuite, la Gson().fromJsonméthode a besoin du type spécifié pour le résultat de la fonction (qui doit correspondre à celui TypeTokencréé). Deux versions de ce travail, comme ci-dessus ou:

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

Pour faciliter la création du, TypeTokenvous pouvez créer une fonction d'assistance, qui doit être en ligne pour pouvoir utiliser des paramètres de type réifiés :

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

Qui peut ensuite être utilisé de l'une de ces manières:

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

Et l'ensemble du processus peut être enveloppé dans une fonction d'extension pour l' Gsoninstance:

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

Pour que vous puissiez simplement appeler Gson et ne pas vous inquiéter du TypeTokentout:

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

Ici, Kotlin utilise l'inférence de type d'un côté de l'affectation ou de l'autre, et des génériques réifiés pour qu'une fonction en ligne passe par le type complet (sans effacement), et l'utilise pour construire un TypeTokenet également faire l'appel à Gson


1
Salut @Jayson, je ne pourrais pas le faire fonctionner aussi amusant en ligne dans Android Studio. Ça semble aller mais ce n'est pas reconnu quand je le faisGson().fromJson<kotlin.List<Turns>>(pref.turns)
Juan Saravia

@juancho pouvez-vous me dire ce que signifie «non reconnu»? Une erreur de compilation? Vous avez la méthode d'extension importée et disponible d'en haut?
Jayson Minard

2
Je copie et colle votre code dans Android Studio et ai importé le plaisir dans ma classe kotlin. J'ai essayé de faire ce que vous avez dit mais pour une raison quelconque, le compilateur m'a dit que ce plaisir n'existe pas. J'utilise déjà d'autres fonctions d'extention mais je ne sais pas ce que votre suggestion ne fonctionne pas. Quelle version d'AS et de Kotlin utilisez-vous? juste pour essayer à nouveau.
Juan Saravia

Ce n'est pas directement lié au studio Android, Kotlin est le même à l'intérieur ou à l'extérieur de cela. Créez-vous une instance de Gson()ou simplement Gsoncomme si elle était statique? Vous avez besoin du premier, une instance.
Jayson Minard le

21

Une autre option (pas sûr qu'elle ait l'air plus élégante que les autres) pourrait être un appel comme celui-ci:

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

Vous utilisez donc la doublure java Array class one au lieu de "pure Kotlin".


2
Étant donné que TypeToken ne fonctionne pas sur tous les téléphones fiables, c'est la meilleure solution pour moi. Une doublure simple avec du kotlin pur.
LuckyMalaka

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

C'est ma façon d'analyser le tableau de données dans kotlin.


Telle devrait être la réponse acceptée, courte et simple.
Umar Ata le

6

J'ai utilisé quelque chose comme ça pour convertir Tà stringet Stringrevenir à l' Tutilisation Gson. Pas exactement ce que vous recherchez mais juste au cas où.

Déclaration d'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
        }
    }

5

Cela fonctionne également et est plus simple

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

Le type de retour doit être nullable. Sinon, le code Java de la bibliothèque Gson peut retourner null, mais Kotlin suppose que le type est non nullable. En conséquence, vous obtenez NPE à Kotlin.
fdermishin

1

Kotlin generic reified functionde Gson désérialise pour ArrayList<T>utiliser ce 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
}
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.