Як користуватися TypeToken + generics з Gson у Котліні


108

Я не можу отримати Список загального типу з користувацького класу (Повороти):

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

в ньому було сказано:

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

Відповіді:


236

Створіть цю вбудовану забаву:

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

і тоді ви можете назвати це таким чином:

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

Попередні альтернативи:

АЛЬТЕРНАТИВ 1:

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

Ви повинні поставити object :і конкретний типfromJson<List<Turns>>


АЛЬТЕРНАТИВ 2:

Як згадати @cypressious, це можна досягти також таким чином:

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

використовувати як:

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

4
Ви також можете створити хелперний метод, який робить це для вас:inline fun <reified T> genericType() = object: TypeToken<T>() {}.type
Кирило Рахман,

1
або навіть розширює Gson, щоб мати нове перевантаження від Джона, що це робить. Kotlin призначений для розширення, тому розширюйте Gson, щоб зробити його приємніше і сховати TypeTokens.
Джейсон Мінард

Я зробив запропоновану редакцію, яка робить відповідь більш повною та формальною, оскільки цю відповідь, мабуть, побачать багато хто використовує Gson. Я додав пояснення у відповідь та посилання на посилання Котліна на теми, які використовуються для вирішення проблеми ... так що людям не доведеться читати всі інші відповіді чи коментарі, які стосувалися цього. Якщо ви приймаєте правки, я можу видалити свою відповідь нижче.
Джейсон Мінард

Редагування відхилено, див. Мою відповідь нижче для повної версії, яка поєднує всі відповіді та коментарі в одну цілісну відповідь. Ви прийняли власну відповідь, але вона не є повною.
Джейсон Мінард

видалення попередження kotlin: inline fun <reified T> genericType (): Тип? = об’єкт: TypeToken <T> () {} .type
Хуан Мендес,

28

Це вирішує проблему:

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

Перший рядок створює вираження об'єкта, яке сходить з, TypeTokenа потім отримує від цього Java Type. Тоді Gson().fromJsonметоду або потрібен тип, вказаний для результату функції (який повинен відповідати TypeTokenствореному). Дві версії цього твору, як вище, або:

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

Щоб полегшити створення, TypeTokenви можете створити допоміжну функцію, яка повинна бути вбудованою, щоб вона могла використовувати параметри повторного типу :

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

Потім їх можна використовувати будь-яким із цих способів:

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

І весь процес можна перетворити на функцію розширення для Gsonекземпляра:

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

Так що ви можете просто зателефонувати в Gson і зовсім не турбуватися про TypeToken:

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

Тут Котлін використовує висновок типу з однієї сторони присвоєння або з іншого, і вдосконалив дженерики для вбудованої функції, щоб пройти через повний тип (без стирання), і використовуючи цю функцію для побудови а, TypeTokenа також для виклику в Gson


1
Привіт @Jayson, я не можу зробити так, щоб це працювало в Inline Studio. Здається, все гаразд, але це не визнається, коли я це робитиGson().fromJson<kotlin.List<Turns>>(pref.turns)
Хуан Саравія

@juancho Ви можете мені сказати, що означає "не визнано"? Помилка компілятора? У вас імпортований і доступний зверху метод розширення?
Джейсон Мінард

2
Я копіюю і вставляю ваш код в Android Studio і імпортую задоволення в свій клас kotlin. Я намагався зробити те, що ви сказали, але чомусь компілятор сказав мені, що цієї забави не існує. Я вже використовую інші функції розширення, але я не знаю, що ваша пропозиція не працює. Яку версію AS та Kotlin ви використовуєте? просто спробувати ще раз.
Хуан Саравія

Це не пов’язано безпосередньо зі студією Android, Котлін - це всередині або зовні. Ви створюєте екземпляр Gson()чи просто Gsonтак, якби він статичний? Вам потрібен перший, екземпляр.
Джейсон Мінард

21

Іншим варіантом (не впевнений, що він виглядає елегантніше інших) може бути такий дзвінок:

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

Отже, ви використовуєте один лайнер класу java Array, а не "чистий Котлін".


2
Оскільки TypeToken не працює на кожному телефоні надійно, це найкраще рішення для мене. Простий вкладиш з чистим котліном.
LuckyMalaka

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

Це мій спосіб розбору масиву даних у kotlin.


Це має бути прийнята відповідь, коротка та проста.
Умар Ата

6

Я використав щось подібне для перетворення Tна string& Stringназад до Tвикористання Gson. Не зовсім те, що ви шукаєте, але про всяк випадок.

Оголошення розширення

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)

Використання

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

Це працює і простіше

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

Тип повернення повинен бути нульовим. Інакше код Java в бібліотеці Gson може повернути нуль, але Котлін припускає, що тип не є нульовим. В результаті ви отримуєте NPE в Котліні.
fdermishin

1

Котлін generic reified functionз Гсона десеріалізують, щоб ArrayList<T>використовувати цей код

 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
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.