Jak używać typów generycznych TypeToken + z Gson w Kotlin


108

Nie mogę uzyskać listy typu ogólnego z klasy niestandardowej (obroty):

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

powiedziało:

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

Odpowiedzi:


237

Utwórz tę wbudowaną zabawę:

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

a potem możesz to nazwać w ten sposób:

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

Poprzednie alternatywy:

ALTERNATYWA 1:

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

Musisz umieścić object :i konkretny typfromJson<List<Turns>>


ALTERNATYWA 2:

Jak wspomina @cypressious, można to osiągnąć również w ten sposób:

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

Użyj jako:

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

4
Możesz także stworzyć pomocniczą metodę, która zrobi to za Ciebie:inline fun <reified T> genericType() = object: TypeToken<T>() {}.type
— Kirill Rakhman,

1
lub nawet rozszerza Gson o nowe przeciążenie fromJson, które to robi. Kotlin jest zaprojektowany do rozszerzania, więc rozszerz Gson, aby był ładniejszy i ukryj TypeTokens.
— Jayson Minard

Dokonałem sugerowanej zmiany, która sprawia, że ​​odpowiedź jest bardziej kompletna i formalna, ponieważ ta odpowiedź jest prawdopodobnie widziana przez wielu użytkowników Gson. Dodałem wyjaśnienia w odpowiedzi i linki do odniesień Kotlin do tematów używanych do rozwiązania problemu ... aby ludzie nie musieli czytać wszystkich innych odpowiedzi lub komentarzy, które weszły w to. Jeśli zaakceptujesz zmianę, mogę usunąć moją odpowiedź poniżej.
— Jayson Minard

Edycja odrzucona, zobacz moją odpowiedź poniżej, aby uzyskać pełną wersję, która łączy wszystkie odpowiedzi i komentarze w jedną spójną odpowiedź. Przyjąłeś własną odpowiedź, ale nie jest kompletna.
— Jayson Minard

usuwanie ostrzeżenia kotlin: inline fun <reified T> genericType (): Type? = obiekt: TypeToken <T> () {} .type
— Juan Mendez

28

To rozwiązuje problem:

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

Pierwsza linia tworzy wyrażenie obiektowe, które pochodzi od, TypeTokena następnie pobiera z niego Javę Type. Następnie Gson().fromJsonmetoda wymaga albo typu określonego dla wyniku funkcji (który powinien pasować do TypeTokenutworzonej). Dwie wersje tej pracy, jak wyżej lub:

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

Aby ułatwić tworzenie TypeToken, możesz utworzyć funkcję pomocniczą, która musi być wbudowana, aby mogła używać parametrów typu reified :

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

Które można następnie wykorzystać na jeden z następujących sposobów:

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

Cały proces można opakować w funkcję rozszerzającą dla Gsoninstancji:

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

Abyś mógł po prostu zadzwonić do Gson i wcale się nie martwić TypeToken:

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

Tutaj Kotlin używa wnioskowania o typie z jednej lub drugiej strony przypisania i reified generics dla funkcji inline, która przechodzi przez pełny typ (bez wymazywania) i używa tego do skonstruowania a, TypeTokena także wywołania Gson


1
Cześć @Jayson, nie mogę sprawić, by działało to tak fajnie w Android Studio. Wydaje się, że jest w porządku, ale nie jest rozpoznawany, kiedy to robięGson().fromJson<kotlin.List<Turns>>(pref.turns)
— Juan Saravia

@juancho, czy możesz mi powiedzieć, co oznacza „nierozpoznany”? Błąd kompilatora? Masz metodę rozszerzenia zaimportowaną i dostępną z góry?
— Jayson Minard

2
Kopiuję i wklejam twój kod w Android Studio i zaimportowałem zabawę do mojej klasy kotlin. Próbowałem zrobić to, co powiedziałeś, ale z jakiegoś powodu kompilator powiedział mi, że taka zabawa nie istnieje. Używam już innych funkcji rozszerzających, ale nie wiem, co Twoja sugestia nie działa. Której wersji AS i Kotlin używasz? po prostu spróbować ponownie.
— Juan Saravia

Nie jest to bezpośrednio związane ze studiem Android, Kotlin jest taki sam wewnątrz lub na zewnątrz. Tworzysz instancję, Gson()czy po prostu Gsontak jakby była statyczna? Potrzebujesz pierwszej instancji.
— Jayson Minard

21

Inną opcją (nie jestem pewien, czy wygląda bardziej elegancko niż inne) może być takie połączenie:

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

Więc używasz java Array class one liner zamiast "czystego Kotlin".


2
Ponieważ TypeToken nie działa na każdym telefonie niezawodnie, jest to dla mnie najlepsze rozwiązanie. Łatwy jeden liner z czystym kotlinem.
— LuckyMalaka

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

To mój sposób na parsowanie tablicy danych w kotlin.


To powinna być przyjęta odpowiedź, krótka i prosta.
— Umar Ata

6

Kiedyś coś takiego do konwersji Tdo stringi Stringz powrotem do Tkorzystania Gson. Nie do końca to, czego szukasz, ale na wszelki wypadek.

Deklarowanie rozszerzenia

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)

Stosowanie

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

To również działa i jest prostsze

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

Zwracany typ musi mieć wartość null. W przeciwnym razie kod Java w bibliotece Gson może zwrócić wartość null, ale Kotlin zakłada, że ​​typ nie dopuszcza wartości null. W rezultacie otrzymujesz NPE w Kotlinie.
— fdermishin

1

Kotlin generic reified functionz Gson deserializacji, aby ArrayList<T>użyć tego kodu

 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
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.