gson 房间通用类型转换器

hgqdbh6s  于 2023-03-02  发布在  其他
关注(0)|答案(1)|浏览(169)

如果有人可以,请解释为什么这个TypeConverter工作:

import androidx.room.TypeConverter
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken

abstract class SetConverter <T>{
    private val gson = Gson()
    private val setType = object: TypeToken<Set<T>>(){}.type

    @TypeConverter
    fun toJson(set: Set<T>): String{
        return gson.toJson(set, setType)
    }

    @TypeConverter
    fun fromJson(json: String): Set<T>{
        return gson.fromJson(json, setType)
    }
}

在实体中,我通过以下实现使用SetConverter:

class CalendarSetConverter: SetConverter<Calendar>()

这很管用。
但是类的类似TypeConverter不起作用。

import androidx.room.TypeConverter
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken

abstract class ClassConverter<T> {
    private val gson = Gson()
    private val type = object : TypeToken<T>(){}.type

    @TypeConverter
    fun toJson(value: T): String{
        return gson.toJson(value, type)
    }

    @TypeConverter
    open fun fromJson(json: String): T{
        return gson.fromJson(json, type)
    }
}

实体执行:

class CalendarConverter : ClassConverter<Calendar>()

从数据库读取日历时,此实现在运行时抛出错误:
Caused by: java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to java.util.Calendar
如何为房间创建通用的TypeConverter?

qaxu7uf2

qaxu7uf21#

实际上,您的CalendarSetConverter很可能也没有按照您的预期运行,但由于类型擦除,您很可能还没有注意到这个问题。如果您尝试使用CalendarSetConverter返回的Set中的元素,您也会遇到ClassCastException

val set = CalendarSetConverter().fromJson(json);
// ClassCastException: LinkedTreeMap cannot be cast to class Calendar
val calendar: Calendar = set.first()

这两种方法的潜在问题也是类型擦除。你永远不应该创建一个object : TypeToken<...>来捕获一个类型参数(例如T),除非那个类型参数被具体化。否则在编译时使用类型参数的上限,或者Any(如果没有明确指定上限)。所以你的代码实际上看起来是这样的:
x一个一个一个一个x一个一个二个x
Gson将Any(分别为java.lang.Object)反序列化为Map,更准确地说是其内部类LinkedTreeMap,这导致了您所遇到的ClassCastException。这不适用于序列化(toJson),其中Gson在序列化Any时将使用运行时类型。
要解决此问题,您必须将TypeToken作为构造函数参数,例如:

abstract class ClassConverter<T>(
    val typeToken: TypeToken<T>
) {
    private val gson = Gson()

    @TypeConverter
    fun toJson(value: T): String {
        return gson.toJson(value, typeToken.type)
    }

    @TypeConverter
    fun fromJson(json: String): T {
        // Note: This uses the fromJson(String, TypeToken) overload added in Gson 2.10
        return gson.fromJson(json, typeToken)
    }
}

然后创建转换器子类,如下所示:

class CalendarConverter : ClassConverter<Calendar>(TypeToken.get(Calendar::class.java))
class CalendarSetConverter : ClassConverter<Set<Calendar>>(object : TypeToken<Set<Calendar>>() {})

不过我不知道这是否适用于Room,也许this question提供了更好的解决方案。

相关问题