如何在 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&lt;List&lt;Turns&gt;&gt;


备选方案 2:

正如@cypressious 提到的,它也可以通过这种方式实现:

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

用作:

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

【讨论】:

你也可以创建一个辅助方法来为你做这件事:inline fun &lt;reified T&gt; genericType() = object: TypeToken&lt;T&gt;() .type 甚至扩展 Gson 以拥有一个新的 fromJson 重载来执行此操作。 Kotlin 旨在扩展,因此扩展 Gson 以使其更好并隐藏 TypeToken。 我做了一个建议的编辑,使答案更加完整和正式,因为许多使用 Gson 的人可能会看到这个答案。我在答案中添加了解释,以及用于解决问题的主题的 Kotlin 参考链接……因此人们不必阅读所有其他答案或涉及此问题的 cmets。如果您接受编辑,我可以在下面删除我的答案。 编辑被拒绝,请参阅下面的我的答案以获得将所有答案和 cmets 组合成一个连贯答案的完整版本。您接受了自己的答案,但它并不完整。 移除 kotlin 警告:inline fun genericType() : Type? = 对象:TypeToken() .type【参考方案2】:

这样就解决了问题:

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&lt;kotlin.List&lt;Turns&gt;&gt;(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 转换为stringString 使用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&lt;T&gt; 使用此代码

 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 中将 String 转换为 Int?

如何在 Kotlin 中将数据类转换为 ByteBuffer?

如何在 Kotlin 中将 ArrayList 转换为 JSONArray()

如何在 Binding Adapter 中将此 java 代码写入 kotlin

如何在 Kotlin 中将 View 转换为 Imageview [重复]