Как использовать дженерики TypeToken + с Gson в Kotlin

108

Я не могу получить список универсального типа из настраиваемого класса (повороты):

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

он сказал:

cannot access '<init>' it is 'public /*package*/' in 'TypeToken'
Хуан Саравиа
источник

Ответы:

237

Создайте это встроенное развлечение:

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, чтобы иметь новую перегрузку fromJson, которая делает это. Kotlin предназначен для расширения, поэтому расширьте Gson, чтобы сделать его лучше и скрыть TypeTokens.
Джейсон Минард,
Я внес предложенное изменение, которое делает ответ более полным и формальным, поскольку этот ответ, вероятно, увидят многие, кто использует Gson. Я добавил пояснения в ответ и ссылки на ссылки Kotlin по темам, используемым для решения проблемы ... так что людям не нужно читать все остальные ответы или комментарии, которые касались этого. Если вы примете изменение, я могу удалить свой ответ ниже.
Джейсон Минард
Редактирование отклонено, см. Мой ответ ниже, чтобы получить полную версию, которая объединяет все ответы и комментарии в один последовательный ответ. Вы приняли свой ответ, но он не полон.
Джейсон Минард
удаление предупреждения kotlin: inline fun <reified T> genericType (): Type? = object: 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)

Здесь Kotlin использует вывод типа с одной стороны присваивания или другой, и повторно использует универсальные шаблоны для встроенной функции, чтобы пройти через полный тип (без стирания), и использовать это для создания, TypeTokenа также для вызова Gson

Джейсон Минард
источник
1
Привет, @Jayson, я не мог заставить его работать в Android Studio так встраиваемым образом. Кажется, все в порядке, но это не распознается, когда я это делаюGson().fromJson<kotlin.List<Turns>>(pref.turns)
Хуан Саравиа
@juancho, ты можешь сказать мне, что означает "не распознано"? Ошибка компилятора? У вас есть импортированный и доступный сверху метод расширения?
Джейсон Минард
2
Я копирую и вставляю ваш код в Android Studio и импортировал удовольствие в свой класс kotlin. Я пытался сделать то, что вы сказали, но компилятор почему-то сказал мне, что этой забавы не существует. Я уже использую другие функции расширения, но не знаю, что ваше предложение не работает. Какую версию AS и Kotlin вы используете? просто чтобы попробовать еще раз.
Хуан Саравиа
Это не связано напрямую со студией Android, Kotlin внутри и снаружи одинаков. Вы создаете экземпляр Gson()или просто Gsonстатичный? Вам нужен первый экземпляр.
Джейсон Минард,
21

Другой вариант (не уверен, что он выглядит более элегантно, чем другие) может заключаться в следующем:

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

Итак, вы используете один лайнер класса java Array вместо «чистого Kotlin».

Тобиас Райх
источник
2
Поскольку TypeToken не работает на каждом надежном телефоне, это лучшее решение для меня. Легкий лайнер с чистым котлином.
LuckyMalaka
11
val obj: MutableList<SaleItemResponse> = Gson().fromJson(messageAfterDecrypt,
    object : TypeToken<List<SaleItemResponse>>() {}.type)

Это мой способ разбора массива данных в котлине.

Toàn Mỹ
источник
Это должен быть принятый ответ, короткий и простой.
Умар Ата
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
        }
    }
harsh_v
источник
5

Это тоже работает, но проще

    inline fun <reified T> Gson.fromJson(json: String) : T = 
         this.fromJson<T>(json, T::class.java)
Кристофер Перри
источник
Тип возвращаемого значения должен допускать значение NULL. В противном случае код Java в библиотеке Gson может возвращать значение NULL, но Kotlin предполагает, что тип не допускает значения NULL. В результате вы получаете NPE в Котлине.
fdermishin
1

Kotlin generic reified functionof Gson десериализует, чтобы 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
}
Мусульманин Шахсаван
источник