Kotlin:指定的types参数会导致Gson失败
我遇到了一个奇怪的行为,使用reified
types的函数内的Gson反序列reified
。 只有当interfaces
涉及到types参数时才会发生。
采取以下代码:
val toBeSerialized = listOf("1337") with(Gson()) { val ser = toJson(toBeSerialized) val deser = fromJson<List>(ser) }
第4行使用自定义扩展函数Gson.fromJson(json: String): T
。
如果T
被定义为物化,它就会失败 :
inline fun Gson.fromJson(json: String): T = fromJson(json, object : TypeToken() {}.type)
如果它被定义为一个正常的types参数,它就可以工作 :
fun Gson.fromJson(json: String): T = fromJson(json, object : TypeToken() {}.type)
(请注意,使T
化在这里没有意义,只是想了解它在特殊用例中的影响)
使用reified
外观时的例外情况如下:
Exception in thread "main" java.lang.RuntimeException: Unable to invoke no-args constructor for ? extends java.io.Serializable. Registering an InstanceCreator with Gson for this type may fix this problem. at com.google.gson.internal.ConstructorConstructor$14.construct(ConstructorConstructor.java:226) at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:210) at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.read(TypeAdapterRuntimeTypeWrapper.java:41) at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:82) at com.google.gson.internal.bind.CollectionTypeAdapterFactory$Adapter.read(CollectionTypeAdapterFactory.java:61) at com.google.gson.Gson.fromJson(Gson.java:888) at com.google.gson.Gson.fromJson(Gson.java:853) at com.google.gson.Gson.fromJson(Gson.java:802) at SoTestsKt.main(SoTests.kt:25) Caused by: java.lang.UnsupportedOperationException: Interface can't be instantiated! Interface name: java.io.Serializable at com.google.gson.internal.UnsafeAllocator.assertInstantiable(UnsafeAllocator.java:117) at com.google.gson.internal.UnsafeAllocator$1.newInstance(UnsafeAllocator.java:49) at com.google.gson.internal.ConstructorConstructor$14.construct(ConstructorConstructor.java:223) ... 8 more
使用reified T
的版本失败了,因为它试图将“1337”反序列化为Serializable
接口,所以它不能被实例化,默认情况下没有可以反序列化成Serializable
types适配器就像List<...>
)。 解决这个问题的最简单方法是将List
作为typesparameter passing。
在非通用版本中没有实际的types信息被传递给你的扩展函数。 您可以通过打印您从令牌获得的types来validation这一点:
fun Gson.fromJson(json: String): T { val tt = object : TypeToken () {}.type; println(tt); return fromJson(json, tt); }
在没有统一的版本中,只会打印T
(即没有实际的types信息可用),但是在reified
版本中,它将打印实际的types(+任何Kotlin声明站点差异修饰符,所以List
变成List extends String>
)。 (我不知道为什么Gson默默地忽略了缺少types信息的这个错误)。
非reified
版本的原因是一个巧合。 由于Gson的反序列化的默认types["1337"]
是ArrayList
,这也是你得到的。 它恰好可以分配给一个List
,并且由于generics被删除,所以String
和Serializable
之间的不匹配types参数没有类别转换exception。 它最终以任何方式运行,因为String
实现了Serializable
。
如果稍微修改示例,例如通过指定不同种类的List
,发生不同的转换,就会遇到麻烦:
val deser = fromJson>(ser)
抛出java.lang.ClassCastException: java.util.ArrayList cannot be cast to java.util.LinkedList
你需要通过reified T
才能传递types信息,但这也意味着失败将会更早发生,并且由于types擦除而不会被忽视,就像在未实现的版本中一样。
正如@Jorn Vernee提到的那样,序列化/反序列化json需要reified
化。 如果没有添加指定,则将被视为List
,并且最终的反序列化对象将具有List
types,并且当您尝试将这些对象用作您的接口时将会出错 。
要序列化/反序列化接口,您需要注册一个自定义适配器来添加已实现类的types信息。 这是一个适配器类,我从这个网站引用,并对其进行一些更改。
class InterfaceAdapter : JsonSerializer , JsonDeserializer { companion object { private val CLASSNAME = "CLASSNAME" private val DATA = "DATA" } @Throws(JsonParseException::class) override fun deserialize(jsonElement: JsonElement, type: Type, jsonDeserializationContext: JsonDeserializationContext): T { val jsonObject = jsonElement.asJsonObject val prim = jsonObject.get(CLASSNAME) as JsonPrimitive val className = prim.asString val klass = getObjectClass(className) return jsonDeserializationContext.deserialize(jsonObject.get(DATA), klass) } override fun serialize(jsonElement: T, type: Type, jsonSerializationContext: JsonSerializationContext): JsonElement { val jsonObject = JsonObject() jsonObject.addProperty(CLASSNAME, jsonElement.javaClass.name) jsonObject.add(DATA, jsonSerializationContext.serialize(jsonElement)) return jsonObject } private fun getObjectClass(className: String): Class<*> { try { return Class.forName(className) } catch (e: ClassNotFoundException) { throw JsonParseException(e.message) } } }
将适配器注册到gson对象,并提供types信息。
inline fun Any.toJson(): String { val builder = GsonBuilder() builder.registerTypeAdapter(Serializable::class.java, InterfaceAdapter()) val gson = builder.create() return gson.toJson(this, object : TypeToken() {}.type) } inline fun fromJson(json: String): T { val builder = GsonBuilder() builder.registerTypeAdapter(Serializable::class.java, InterfaceAdapter()) val gson = builder.create() return gson.fromJson(json, object : TypeToken () {}.type) }
示例代码:
data class User(val name: String) : Serializable val data = listOf(User("Sam")) val ser = data.toJson>() val deser = fromJson>(ser) println(ser) println(deser.get(0)::class) println(deser.get(0)) /* Output [{"CLASSNAME":"User","DATA":{"name":"Sam"}}] class User User(name=Sam) */