2015-12-23 6 views
14

Utilizzando Gson, voglio deserializzare una classe Kotlin che contiene una proprietà lazy.Deserializzare le classi con proprietà lazy usando Gson e Kotlin 1.0 beta 4

Con Kotlin 1.0 beta 4 ottengo il seguente errore durante la deserializzazione oggetto:

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

Con Kotlin 1.0 beta 2, che ho usato per marcare la proprietà con l'annotaiton @Transient dire GSON per saltarlo. Con la beta 4 questo non è più possibile, poiché l'annotazione causa un errore di compilazione.

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

Non riesco a capire come risolvere questo problema. Qualche idea?

Modifica: la proprietà lazy viene serializzata su JSON ("my_lazy_prop$delegate":{}), ma questo non è quello che voglio in quanto è calcolato da altre proprietà. Suppongo che se trovassi un modo per impedire che la proprietà venisse serializzata, il crash di deserializzazione sarebbe stato risolto.

+0

Ti dispiacerebbe cambiare la risposta accettata a il mio, che è più aggiornato e ha molti più voti? –

+1

Sure @FabianZeindl – clemp6r

risposta

23

Dal Kotlin 1.0 semplicemente contrassegnare il campo come questo di ignorarlo durante de/serializzazione:

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

Il motivo è che il campo delegate non è un campo di supporto in realtà quindi era vietato. Una delle soluzioni è quella di implementare ExclusionStrategy: https://stackoverflow.com/a/27986860/1460833

Qualcosa del genere:

@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() 

Vedere legati biglietto https://youtrack.jetbrains.com/issue/KT-10502

L'altra soluzione è di serializzare i valori pigri così:

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") 
} 

Esempio di utilizzo:

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) 
} 

che produrrà il seguente output:

{"f":1,"o":0} 
C(o=0) 
1 
+0

Ho copiato il codice, registrato ExclusionStrategy in Gson, contrassegnato il campo pigro con l'annotazione @GsonTransient -> nessun effetto. Ho dimenticato qualcosa? – clemp6r

+0

forse hai dimenticato di impostarlo su gson builder –

+0

Suggerimento: f.getAnnotazione viene chiamata per il campo, ma restituisce null – clemp6r

Problemi correlati