使用 Gson 和 Kotlin 1.0 beta 4 使用惰性属性反序列化 类
Deserializing classes with lazy properties using Gson and Kotlin 1.0 beta 4
使用 Gson,我想反序列化包含惰性 属性 的 Kotlin class。
使用 Kotlin 1.0 beta 4 我在对象反序列化期间遇到以下错误:
Caused by: java.lang.InstantiationException: can't instantiate class kotlin.Lazy
在 Kotlin 1.0 beta 2 中,我曾经用 @Transient annotaiton 标记 属性 以告诉 Gson 跳过它。在 beta 4 中,这是不可能的,因为注释会导致编译错误。
This annotation is not applicable to target 'member property without backing field'
我不知道如何解决这个问题。有什么想法吗?
编辑:惰性 属性 被序列化为 JSON ("my_lazy_prop$delegate":{}
),但这不是我想要的,因为它是根据其他属性计算的。我想如果我找到一种方法来防止 属性 被序列化,反序列化崩溃就会得到修复。
原因是 delegate
字段实际上不是支持字段,所以它被禁止了。解决方法之一是实施 ExclusionStrategy
:
类似的东西:
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FIELD, AnnotationTarget.PROPERTY)
annotation class GsonTransient
object TransientExclusionStrategy : ExclusionStrategy {
override fun shouldSkipClass(type: Class<*>): Boolean = false
override fun shouldSkipField(f: FieldAttributes): Boolean =
f.getAnnotation(GsonTransient::class.java) != null
|| f.name.endsWith("$delegate")
}
fun gson() = GsonBuilder()
.setExclusionStrategies(TransientExclusionStrategy)
.create()
查看相关工单https://youtrack.jetbrains.com/issue/KT-10502
另一种解决方法是同时序列化惰性值:
object SDForLazy : JsonSerializer<Lazy<*>>, JsonDeserializer<Lazy<*>> {
override fun serialize(src: Lazy<*>, typeOfSrc: Type, context: JsonSerializationContext): JsonElement =
context.serialize(src.value)
override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): Lazy<*> =
lazyOf<Any?>(context.deserialize(json, (typeOfT as ParameterizedType).actualTypeArguments[0]))
}
class KotlinNamingPolicy(val delegate: FieldNamingStrategy = FieldNamingPolicy.IDENTITY) : FieldNamingStrategy {
override fun translateName(f: Field): String =
delegate.translateName(f).removeSuffix("$delegate")
}
用法示例:
data class C(val o: Int) {
val f by lazy { 1 }
}
fun main(args: Array<String>) {
val gson = GsonBuilder()
.registerTypeAdapter(Lazy::class.java, SDForLazy)
.setFieldNamingStrategy(KotlinNamingPolicy())
.create()
val s = gson.toJson(C(0))
println(s)
val c = gson.fromJson(s, C::class.java)
println(c)
println(c.f)
}
将产生以下输出:
{"f":1,"o":0}
C(o=0)
1
由于 Kotlin 1.0 只需像这样标记字段即可在 de/serialization 期间忽略它:
@delegate:Transient
val field by lazy { ... }
正如其他答案所解释的那样,不应序列化委托字段。
您可以在委托字段中使用 transient
实现此目的,正如@Fabian Zeindl 所建议的那样:
@delegate:Transient
val field by lazy { ... }
或跳过 GsonBuilder
中的所有委托字段,如@Sergey Mashkov 所提议:
GsonBuilder().setExclusionStrategies(object : ExclusionStrategy {
override fun shouldSkipClass(type: Class<*>): Boolean = false
override fun shouldSkipField(f: FieldAttributes): Boolean = f.name.endsWith("$delegate")
}
但是,如果您的 class 没有无参数构造函数,您可能会遇到 NullPointerException
。
发生这种情况是因为当 Gson 找不到无参数构造函数时,它将使用 ObjectConstructor
和 UnsafeAllocator
使用反射来构造您的对象。 (参见 )。这将清除委托字段的 Kotlin 创建。
要修复它,请在 class 中创建一个无参数构造函数,或者使用 Gson InstanceCreator
为 Gson 提供默认对象。
GsonBuilder().registerTypeAdapter(YourClass::class, object : InstanceCreator<YourClass> {
override fun createInstance(type: Type?) = YourClass("defaultValue")
})
使用 Gson,我想反序列化包含惰性 属性 的 Kotlin class。
使用 Kotlin 1.0 beta 4 我在对象反序列化期间遇到以下错误:
Caused by: java.lang.InstantiationException: can't instantiate class kotlin.Lazy
在 Kotlin 1.0 beta 2 中,我曾经用 @Transient annotaiton 标记 属性 以告诉 Gson 跳过它。在 beta 4 中,这是不可能的,因为注释会导致编译错误。
This annotation is not applicable to target 'member property without backing field'
我不知道如何解决这个问题。有什么想法吗?
编辑:惰性 属性 被序列化为 JSON ("my_lazy_prop$delegate":{}
),但这不是我想要的,因为它是根据其他属性计算的。我想如果我找到一种方法来防止 属性 被序列化,反序列化崩溃就会得到修复。
原因是 delegate
字段实际上不是支持字段,所以它被禁止了。解决方法之一是实施 ExclusionStrategy
:
类似的东西:
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.FIELD, AnnotationTarget.PROPERTY)
annotation class GsonTransient
object TransientExclusionStrategy : ExclusionStrategy {
override fun shouldSkipClass(type: Class<*>): Boolean = false
override fun shouldSkipField(f: FieldAttributes): Boolean =
f.getAnnotation(GsonTransient::class.java) != null
|| f.name.endsWith("$delegate")
}
fun gson() = GsonBuilder()
.setExclusionStrategies(TransientExclusionStrategy)
.create()
查看相关工单https://youtrack.jetbrains.com/issue/KT-10502
另一种解决方法是同时序列化惰性值:
object SDForLazy : JsonSerializer<Lazy<*>>, JsonDeserializer<Lazy<*>> {
override fun serialize(src: Lazy<*>, typeOfSrc: Type, context: JsonSerializationContext): JsonElement =
context.serialize(src.value)
override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): Lazy<*> =
lazyOf<Any?>(context.deserialize(json, (typeOfT as ParameterizedType).actualTypeArguments[0]))
}
class KotlinNamingPolicy(val delegate: FieldNamingStrategy = FieldNamingPolicy.IDENTITY) : FieldNamingStrategy {
override fun translateName(f: Field): String =
delegate.translateName(f).removeSuffix("$delegate")
}
用法示例:
data class C(val o: Int) {
val f by lazy { 1 }
}
fun main(args: Array<String>) {
val gson = GsonBuilder()
.registerTypeAdapter(Lazy::class.java, SDForLazy)
.setFieldNamingStrategy(KotlinNamingPolicy())
.create()
val s = gson.toJson(C(0))
println(s)
val c = gson.fromJson(s, C::class.java)
println(c)
println(c.f)
}
将产生以下输出:
{"f":1,"o":0}
C(o=0)
1
由于 Kotlin 1.0 只需像这样标记字段即可在 de/serialization 期间忽略它:
@delegate:Transient
val field by lazy { ... }
正如其他答案所解释的那样,不应序列化委托字段。
您可以在委托字段中使用 transient
实现此目的,正如@Fabian Zeindl 所建议的那样:
@delegate:Transient
val field by lazy { ... }
或跳过 GsonBuilder
中的所有委托字段,如@Sergey Mashkov 所提议:
GsonBuilder().setExclusionStrategies(object : ExclusionStrategy {
override fun shouldSkipClass(type: Class<*>): Boolean = false
override fun shouldSkipField(f: FieldAttributes): Boolean = f.name.endsWith("$delegate")
}
但是,如果您的 class 没有无参数构造函数,您可能会遇到 NullPointerException
。
发生这种情况是因为当 Gson 找不到无参数构造函数时,它将使用 ObjectConstructor
和 UnsafeAllocator
使用反射来构造您的对象。 (参见 )。这将清除委托字段的 Kotlin 创建。
要修复它,请在 class 中创建一个无参数构造函数,或者使用 Gson InstanceCreator
为 Gson 提供默认对象。
GsonBuilder().registerTypeAdapter(YourClass::class, object : InstanceCreator<YourClass> {
override fun createInstance(type: Type?) = YourClass("defaultValue")
})