Kotlin:指定的types参数会导致Gson失败

我遇到了一个奇怪的行为,使用reifiedtypes的函数内的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接口,所以它不能被实例化,默认情况下没有可以反序列化成Serializabletypes适配器就像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 )。 (我不知道为什么Gson默默地忽略了缺少types信息的这个错误)。

reified版本的原因是一个巧合。 由于Gson的反序列化的默认types["1337"]ArrayList ,这也是你得到的。 它恰好可以分配给一个List ,并且由于generics被删除,所以StringSerializable之间的不匹配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 ,并且最终的反序列化对象将具有Listtypes,并且当您尝试将这些对象用作您的接口时将会出错 。

要序列化/反序列化接口,您需要注册一个自定义适配器来添加已实现类的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) */