Verwendung von TypeToken + Generika mit Gson in Kotlin


106

Ich kann keine Liste mit generischen Typen aus einer benutzerdefinierten Klasse (Turns) abrufen:

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

es sagte:

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

Antworten:


233

Erstellen Sie diesen Inline-Spaß:

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

und dann können Sie es so nennen:

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

Bisherige Alternativen:

ALTERNATIVE 1:

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

Sie müssen eingeben object :und den spezifischen Typ eingebenfromJson<List<Turns>>


ALTERNATIVE 2:

Wie @cypressious erwähnt, kann dies auch auf folgende Weise erreicht werden:

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

benutzen als:

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

3
Sie können auch eine inline fun <reified T> genericType() = object: TypeToken<T>() {}.type
Hilfsmethode

1
oder erweitert Gson sogar um eine neue Überladung von fromJson, die dies tut. Kotlin wurde entwickelt, um zu erweitern, also erweitern Sie Gson, um es schöner zu machen und TypeTokens zu verstecken.
Jayson Minard

Ich habe eine vorgeschlagene Änderung vorgenommen, die die Antwort vollständiger und formeller macht, da diese Antwort wahrscheinlich von vielen gesehen wird, die Gson verwenden. Ich habe der Antwort Erklärungen und Links zu Kotlin-Referenzen für Themen hinzugefügt, die zur Lösung des Problems verwendet wurden. Die Leute müssen also nicht alle anderen Antworten oder Kommentare lesen, die dazu geführt haben. Wenn Sie die Bearbeitung akzeptieren, kann ich meine Antwort unten entfernen.
Jayson Minard

Bearbeiten abgelehnt, siehe meine Antwort unten für eine Vollversion, die alle Antworten und Kommentare in einer zusammenhängenden Antwort kombiniert. Sie haben Ihre eigene Antwort akzeptiert, aber sie ist nicht vollständig.
Jayson Minard

Kotlin-Warnung entfernen: Inline-Spaß <reified T> genericType (): Typ? = Objekt: TypeToken <T> () {} .type
Juan Mendez

28

Dies löst das Problem:

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

Die erste Zeile erstellt einen Objektausdruck , der von diesem abstammt TypeTokenund dann Java Typeabruft. Dann benötigt die Gson().fromJsonMethode entweder den für das Ergebnis der Funktion angegebenen Typ (der mit dem TypeTokenerstellten übereinstimmen sollte ). Zwei Versionen dieser Arbeit, wie oben oder:

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

Um das Erstellen der zu vereinfachen, TypeTokenkönnen Sie eine Hilfsfunktion erstellen, die inline sein muss, damit sie Parameter mit geändertem Typ verwenden kann :

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

Was dann auf eine der folgenden Arten verwendet werden kann:

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

Und der gesamte Prozess kann in eine Erweiterungsfunktion für die GsonInstanz eingebunden werden :

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

Damit Sie einfach Gson anrufen können und sich überhaupt keine Sorgen machen müssen TypeToken:

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

Hier verwendet Kotlin die Typinferenz von der einen oder der anderen Seite der Zuweisung und reifizierte Generika für eine Inline-Funktion, um den vollständigen Typ (ohne Löschung) zu durchlaufen, und verwendet diese, um einen zu konstruieren TypeTokenund auch Gson aufzurufen


1
Hallo @Jayson, ich konnte nicht dafür sorgen, dass dieser Inline-Spaß in Android Studio funktioniert. Scheint in Ordnung zu sein, aber es wird nicht erkannt, wenn ich es tueGson().fromJson<kotlin.List<Turns>>(pref.turns)
Juan Saravia

@juancho kannst du mich anrufen was "nicht erkannt" bedeutet? Ein Compilerfehler? Sie haben die Erweiterungsmethode importiert und sind von oben verfügbar?
Jayson Minard

2
Ich kopiere und füge deinen Code in Android Studio ein und importiere den Spaß in meine Kotlin-Klasse. Ich habe versucht, das zu tun, was Sie gesagt haben, aber aus irgendeinem Grund hat mir der Compiler gesagt, dass dieser Spaß nicht existiert. Ich verwende bereits andere Erweiterungsfunktionen, weiß aber nicht, was Ihr Vorschlag nicht funktioniert. Welche Version von AS und Kotlin verwenden Sie? nur um es noch einmal zu versuchen.
Juan Saravia

Dies hängt nicht direkt mit Android Studio zusammen, Kotlin ist innerhalb oder außerhalb davon gleich. Erstellen Sie eine Instanz von Gson()oder nur so, Gsonals ob sie statisch wäre? Sie benötigen die erste, eine Instanz.
Jayson Minard

21

Eine andere Option (nicht sicher, ob sie eleganter aussieht als die anderen) könnte ein Anruf wie dieser sein:

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

Sie verwenden also den Java Array Class One Liner anstelle von "pure Kotlin".


2
Da TypeToken nicht auf jedem Telefon zuverlässig funktioniert, ist dies die beste Lösung für mich. Ein einfacher Einzeiler mit reinem Kotlin.
LuckyMalaka

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

Es ist meine Art, Datenarrays in Kotlin zu analysieren.


Dies sollte die akzeptierte Antwort sein, kurz und einfach.
Umar Ata

6

Ich habe so etwas verwendet, um Tzu string& Stringzurück zu Tverwenden Gson. Nicht genau das, wonach Sie suchen, aber nur für den Fall.

Verlängerung erklären

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)

Verwendung

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

Dies funktioniert auch und ist einfacher

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

Der Rückgabetyp muss nullwertfähig sein. Andernfalls kann Java-Code in der Gson-Bibliothek null zurückgeben, Kotlin geht jedoch davon aus, dass der Typ nicht nullwertfähig ist. Als Ergebnis erhalten Sie NPE in Kotlin.
Fdermishin

1

Kotlin generic reified functionvon Gson deserialisiert, ArrayList<T>um diesen Code zu verwenden

 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
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.