2017-09-21 66 views
5

Moshi + Kotlin + SealedClass

sealed class Layer 

data class ShapeLayer(var type: LayerType) : Layer 
data class TextLayer(var type: LayerType) : Layer 
data class ImageLayer(var type: LayerType) : Layer 

LayerType kullanarak deserializing json bir yolu bu nesne olmalıdır hangi tip ayırt etmek için kullanılabilir sadece bazı enum var mı oluyor. Tüm LayerTypes olası her alana sahip

class LayerAdapter{ 
    @FromJson 
    fun fromJson(layerJson: LayerJson): Layer { 
     return when (layerJson.layerType) { 
      LayerType.SHAPE -> PreCompLayer() 
      LayerType.SOLID -> SolidLayer() 
      LayerType.Text -> TextLayer() 
     } 
    } 
} 

LayerJson olacağını nesneyi:

ben Adaptörü bu şekilde eklemek düşündüm.

Şimdi sorun şu:

soyut sınıf com.example.models.layers.Layer serileştirmez Can

Ben arayüzünü kullanmayı deneyin, ama öyle olacağını sanmıyorum Bu boş arayüzü kullanmak için doğru.

+0

Ben sadece '@ ToJson' yöntemi eksik düşünüyorum: Bu modelin bu bölümü için benim son kodudur? Aşağıdaki cevap doğrudur. –

+0

Hayır @ToJson var, örneğin – miszmaniac

+0

hm için çok önemli olmadığını düşündüğüm tüm kodları atladım, o zaman bu cevapla aynı görünüyor. Çalıştırdın mı? –

cevap

0

Benim kod baştan aslında doğru olduğu ortaya çıktı!

Sorun veri Sınıf içi alan bildiriyle oldu:

data class LayerContainer(var/val layers: List<Layer>) 

O val ile çalışır ve var çalışmaz! Kotlin, bir şekilde aşağıdan aşağıya farklı kodlar oluşturur.

@JvmSuppressWildcards var layers: List<Layer> 
4

Evet, böyle layerType göre json ayrıştırmak için özel bir tipi adaptör oluşturabilirsiniz:

İşte
class LayerAdapter { 
    @FromJson 
    fun fromJson(layerJson: LayerJson): Layer = when (layerJson.layerType) { 
     LayerType.SHAPE -> ShapeLayer(layerJson.layerType, layerJson.shape ?: "") 
     LayerType.TEXT -> TextLayer(layerJson.layerType, layerJson.text ?: "") 
     LayerType.IMAGE -> ImageLayer(layerJson.layerType, layerJson.image ?: "") 
    } 

    @ToJson 
    fun toJson(layer: Layer): LayerJson = when (layer) { 
     is ShapeLayer -> LayerJson(layer.type, shape = layer.shape) 
     is TextLayer -> LayerJson(layer.type, text = layer.text) 
     is ImageLayer -> LayerJson(layer.type, image = layer.image) 
     else -> throw RuntimeException("Not support data type") 
    } 
} 

Ben her birine netlik için veri sınıfına bazı değişiklikler (ekstra bir özellik yapmak zorunda Layer tipi, ShapeLayer için örneğin shape):

sealed class Layer 

data class ShapeLayer(val type: LayerType, val shape: String) : Layer() 
data class TextLayer(val type: LayerType, val text: String) : Layer() 
data class ImageLayer(val type: LayerType, val image: String) : Layer() 

//LayerJson contains every possible property of all layers 
data class LayerJson(val layerType: LayerType, val shape: String? = null, val text: String? = null, val image: String? = null) : Layer() 

enum class LayerType { 
    SHAPE, TEXT, IMAGE 
} 

Test kodu:

val moshi = Moshi.Builder() 
     .add(LayerAdapter()) 
     .build() 
val type = Types.newParameterizedType(List::class.java, Layer::class.java) 
val adapter = moshi.adapter<List<Layer>>(type) 

//Convert from json string to List<Layer> 
val layers: List<Layer>? = adapter.fromJson(""" 
    [ 
     {"layerType":"SHAPE", "shape":"I am rectangle"}, 
     {"layerType":"TEXT", "text":"I am text"}, 
     {"layerType":"IMAGE", "image":"I am image"} 
    ] 
""".trimIndent()) 
layers?.forEach(::println) 

//Convert a list back to json string 
val jsonString: String = adapter.toJson(layers) 
println(jsonString) 

Çıktı:

ShapeLayer(type=SHAPE, shape=I am rectangle) 
TextLayer(type=TEXT, text=I am text) 
ImageLayer(type=IMAGE, image=I am image) 
[{"layerType":"SHAPE","shape":"I am rectangle"},{"layerType":"TEXT","text":"I am text"},{"image":"I am image","layerType":"IMAGE"}] 

Düzenleme: Eğer Layer içeren diğer nesne ayrıştırmak çalışırken Her zamanki gibi adaptör ekleyebilir. Eğer böyle bir nesne olduğunu varsayalım:

data class LayerContainer(val layers: List<Layer>) 

Test kodu:

val moshi = Moshi.Builder() 
     .add(LayerAdapter()) 
     .build() 

val adapter = moshi.adapter(LayerContainer::class.java) 
val layerContainer: LayerContainer? = adapter.fromJson(""" 
    { 
     "layers": [ 
      {"layerType":"SHAPE", "shape":"I am rectangle"}, 
      {"layerType":"TEXT", "text":"I am text"}, 
      {"layerType":"IMAGE", "image":"I am image"} 
     ] 
    } 
""".trimIndent()) 
layerContainer?.layers?.forEach(::println) 

val jsonString: String = adapter.toJson(layerContainer) 
println(jsonString) 
+4

Harika cevap. Bunu sev. –

+0

OMG bu kapsamlı bir cevaptır :) Çok teşekkür ederim! Ne yazık ki, bu benim için işe yaramaz :) json'un kökünde katman dizisi yapmak için jsonumu çıkardım ve aslında çalışıyor, ancak sorun başka bir yerde: Benim json şunun gibi görünüyor: {"katmanlar" : [... bu liste ...]} Bu Yazılı liste bağdaştırıcısını oluşturucuya nasıl ekleyeceğimi bilmiyorum? – miszmaniac

+0

@miszmaniac Bu olayın cevabını güncelledim. – BakaWaii