Как использовать TypeToken + generics с Gson в Kotlin
Я не могу получить список универсального типа из пользовательского класса (Turns):
val turnsType = TypeToken<List<Turns>>() {}.type
val turns = Gson().fromJson(pref.turns, turnsType)
он сказал:
cannot access '<init>' it is 'public /*package*/' in 'TypeToken'
Ответы
Ответ 1
Создайте эту забавную игру:
inline fun <reified T> Gson.fromJson(json: String) = this.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>>()
Ответ 2
Это решает проблему:
val turnsType = object : TypeToken<List<Turns>>() {}.type
val turns = Gson().fromJson<List<Turns>>(pref.turns, turnsType)
Первая строка создает выражение object, которое происходит от TypeToken
, а затем получает Java Type
. Тогда методу Gson().fromJson
нужен либо тип, указанный для результата функции (который должен соответствовать созданному TypeToken
). Две версии этой работы, как указано выше, или:
val turns: List<Turns> = Gson().fromJson(pref.turns, turnsType)
Чтобы упростить создание TypeToken
, вы можете создать вспомогательную функцию, которая должна быть inline, чтобы она могла использовать параметры типа reified:
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
Ответ 3
Другим вариантом (не уверен, что он выглядит более элегантно, чем другие) может быть такой вызов:
turns = Gson().fromJson(allPurchasesString, Array<Turns>::class.java).toMutableList()
Таким образом, вы используете лайнер java Array class one вместо "pure Kotlin".
Ответ 4
val obj: MutableList<SaleItemResponse> = Gson().fromJson(messageAfterDecrypt,
object : TypeToken<List<SaleItemResponse>>() {}.type)
Это мой способ разбора массива данных в kotlin.
Ответ 5
Это работает также и проще
inline fun <reified T> Gson.fromJson(json: String) : T =
this.fromJson<T>(json, T::class.java)
Ответ 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
}
}