web-dev-qa-db-fra.com

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

Je ne parviens pas à obtenir une liste de types génériques à 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'
80
Juancho

Créez ce plaisir en ligne:

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

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

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

NOTE : Cette approche n'était pas possible auparavant dans les anciennes versions du plugin Kotlin, mais vous pouvez maintenant l'utiliser.


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 dans fromJson<List<Turns>>


ALTERNATIVE 2:

Comme @cypressious mention, cela peut également être réalisé de la manière suivante:

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

utilisé comme:

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

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 un expression d'objet qui descend de TypeToken et obtient ensuite le Java Type. _ De celui-ci. Ensuite, le Gson().fromJson method nécessite le type spécifié pour le résultat de la fonction (qui doit correspondre à la TypeToken créée). Deux versions de ce travail, comme ci-dessus, ou:

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

Pour faciliter la création de TypeToken, vous pouvez créer une fonction d'assistance qui doit obligatoirement être inline pour pouvoir utiliser paramètres de type réifiés :

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

Ce qui peut alors être utilisé de l'une des manières suivantes:

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

Et l'ensemble du processus peut être encapsulé dans une fonction d'extension pour l'instance Gson:

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 pour le TypeToken:

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'assignation et des génériques réifiés pour qu'une fonction en ligne passe à travers le type complet (sans effacement), et l'utilise pour construire un TypeToken et créer le appel à Gson

25
Jayson Minard

Une autre option (pas sûr que cela paraisse plus élégant que les autres) pourrait être un appel comme celui-ci:

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

Vous utilisez donc le Java Array class one liner au lieu de "pure Kotlin".

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

C’est ma façon d’analyser un tableau de données dans Kotlin.

9
Toàn Mỹ

Cela fonctionne aussi bien, et est plus simple

    inline fun <reified T> Gson.fromJson(json: String) : T = 
         this.fromJson<T>(json, T::class.Java)
4
Christopher Perry

J'ai utilisé quelque chose comme ceci pour convertir T en string & String à T à l'aide de Gson. Pas exactement ce que vous cherchez, 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
        }
    }
3
harsh_v