如何在 Kotlin 中将 TypeToken + 泛型与 Gson 一起使用
Posted
技术标签:
【中文标题】如何在 Kotlin 中将 TypeToken + 泛型与 Gson 一起使用【英文标题】:How to use TypeToken + generics with Gson in Kotlin 【发布时间】:2016-01-27 15:44:26 【问题描述】:我无法从自定义类 (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) = 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>>()
【讨论】:
你也可以创建一个辅助方法来为你做这件事:inline fun <reified T> genericType() = object: TypeToken<T>() .type
甚至扩展 Gson 以拥有一个新的 fromJson 重载来执行此操作。 Kotlin 旨在扩展,因此扩展 Gson 以使其更好并隐藏 TypeToken。
我做了一个建议的编辑,使答案更加完整和正式,因为许多使用 Gson 的人可能会看到这个答案。我在答案中添加了解释,以及用于解决问题的主题的 Kotlin 参考链接……因此人们不必阅读所有其他答案或涉及此问题的 cmets。如果您接受编辑,我可以在下面删除我的答案。
编辑被拒绝,请参阅下面的我的答案以获得将所有答案和 cmets 组合成一个连贯答案的完整版本。您接受了自己的答案,但它并不完整。
移除 kotlin 警告:inline fun 这样就解决了问题:
val turnsType = object : TypeToken<List<Turns>>() .type
val turns = Gson().fromJson<List<Turns>>(pref.turns, turnsType)
第一行创建一个从TypeToken
继承的object expression,然后从中获取Java Type
。然后Gson().fromJson
方法要么需要为函数结果指定的类型(它应该与创建的TypeToken
匹配)。该作品的两个版本,如上或:
val turns: List<Turns> = Gson().fromJson(pref.turns, turnsType)
为了更容易创建TypeToken
,您可以创建一个辅助函数,它必须是inline,以便它可以使用reified type parameters:
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
【讨论】:
嗨@Jayson,我无法让它在android Studio 中发挥这种内联乐趣。似乎还可以,但是当我这样做时无法识别Gson().fromJson<kotlin.List<Turns>>(pref.turns)
@juancho 你能告诉我“未识别”是什么意思吗?编译器错误?你有从上面导入并可用的扩展方法吗?
我将您的代码复制并粘贴到 Android Studio 中,并将乐趣导入到我的 kotlin 类中。我试着按照你说的去做,但由于某种原因,编译器告诉我这种乐趣不存在。我已经在使用其他扩展功能,但我不知道您的建议不起作用。您使用的是哪个版本的 AS 和 Kotlin?只是为了再试一次。
这和Android studio没有直接关系,Kotlin内外都一样。您是在创建Gson()
的实例还是只是Gson
的实例,就好像它是静态的一样?您需要第一个实例。【参考方案3】:
这也很有效,而且更简单
inline fun <reified T> Gson.fromJson(json: String) : T =
this.fromJson<T>(json, T::class.java)
【讨论】:
返回类型必须可以为空。否则 Gson 库中的 Java 代码可以返回 null,但 Kotlin 假定该类型是不可为空的。结果,您在 Kotlin 中获得了 NPE。【参考方案4】:另一个选项(不确定它看起来是否比其他选项更优雅)可能是这样的调用:
turns = Gson().fromJson(stringObject, Array<Turns>::class.java).toMutableList()
因此,您使用的是 java Array 类一列,而不是“纯 Kotlin”。
【讨论】:
由于 TypeToken 不能在每部可靠的手机上工作,这对我来说是最好的解决方案。使用纯 kotlin 的简单衬里。【参考方案5】:val obj: MutableList<SaleItemResponse> = Gson().fromJson(messageAfterDecrypt,
object : TypeToken<List<SaleItemResponse>>() .type)
这是我在 kotlin 中解析数据数组的方式。
【讨论】:
这应该是公认的答案,简短而简单。【参考方案6】:我使用类似的方法将T
转换为string
和String
使用Gson
转换回T
。不完全是您正在寻找的东西,但以防万一。
声明扩展
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
【讨论】:
【参考方案7】:Kotlin generic reified function
的 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
【讨论】:
以上是关于如何在 Kotlin 中将 TypeToken + 泛型与 Gson 一起使用的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Kotlin 中将 Firestore 日期/时间戳转换为日期?
如何在 Kotlin 中将数据类转换为 ByteBuffer?
如何在 Kotlin 中将 ArrayList 转换为 JSONArray()