gson - parse - kotlin @serializedname
Deserializar clases con propiedades perezosas utilizando Gson y Kotlin 1.0 beta 4 (2)
Como Kotlin 1.0 simplemente marca el campo como este para ignorarlo durante la des / serialización:
@delegate:Transient
val field by lazy { ... }
Usando Gson, quiero deserializar una clase Kotlin que contiene una propiedad perezosa.
Con Kotlin 1.0 beta 4 obtengo el siguiente error durante la deserialización de objetos:
Caused by: java.lang.InstantiationException: can''t instantiate class kotlin.Lazy
Con Kotlin 1.0 beta 2, solía marcar la propiedad con @Transient annotaiton para decirle a Gson que la omitiera. Con beta 4 esto ya no es posible, ya que la anotación provoca un error de compilación.
This annotation is not applicable to target ''member property without backing field''
No puedo averiguar cómo solucionar este problema. ¿Algunas ideas?
Edición: la propiedad perezosa se serializa a JSON ( "my_lazy_prop$delegate":{}
), pero esto no es lo que quiero ya que se calcula a partir de otras propiedades. Supongo que si encuentro una manera de evitar que la propiedad se serialice, la falla de deserialización se solucionaría.
La razón es que el campo delegate
no es realmente un campo de respaldo, por lo que estaba prohibido. Una de las soluciones alternativas es implementar ExclusionStrategy
: https://.com/a/27986860/1460833
Algo como eso:
@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()
Ver el ticket relacionado https://youtrack.jetbrains.com/issue/KT-10502
La otra solución es serializar valores perezosos también:
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")
}
Ejemplo de uso:
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)
}
que producirá la siguiente salida:
{"f":1,"o":0}
C(o=0)
1