Подтвердить что ты не робот

Десериализация классов с ленивыми свойствами с использованием Gson и Kotlin 1.0 beta 4

Используя Gson, я хочу десериализовать класс Kotlin, который содержит ленивое свойство.

С Kotlin 1.0 beta 4 я получаю следующую ошибку во время десериализации объекта:

Caused by: java.lang.InstantiationException: can't instantiate class kotlin.Lazy

С Kotlin 1.0 beta 2 я использовал знак @Transient annotaiton, чтобы сообщить Gson пропустить его. С бета-версией 4 это больше невозможно, так как аннотация вызывает ошибку компиляции.

This annotation is not applicable to target 'member property without backing field'

Я не могу понять, как это исправить. Любые идеи?

Изменить: свойство lazy сериализуется в JSON ("my_lazy_prop$delegate":{}), но это не то, что я хочу, поскольку оно вычисляется из других свойств. Я полагаю, что если я найду способ предотвратить сериализацию свойства, будет устранена авария десериализации.

4b9b3361

Ответ 1

Так как Kotlin 1.0 просто помечает поле, подобное этому, чтобы игнорировать его во время de/serialization:

@delegate:Transient 
val field by lazy { ... }

Ответ 2

Причина в том, что поле delegate фактически не является базовым полем, поэтому было запрещено. Одним из обходных решений является реализация ExclusionStrategy: fooobar.com/info/20692/...

Что-то вроде этого:

@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