2017-09-21 62 views
5

Có một cách để deserializing json sử dụngMoshi + 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 chỉ là một số enum có thể được sử dụng để phân biệt loại hình nên đối tượng này có.

tôi nghĩ rằng tôi có thể thêm Adaptor theo cách này:

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

đâu LayerJson sẽ là đối tượng trong đó có mọi lĩnh vực có thể có của tất cả LayerTypes.

Bây giờ vấn đề là:

Không thể serialize lớp trừu tượng com.example.models.layers.Layer

tôi có thể cố gắng sử dụng giao diện, nhưng tôi không nghĩ rằng nó sẽ là chính xác để sử dụng giao diện trống trong này.

+0

Tôi nghĩ bạn đã bỏ lỡ phương pháp '@ ToJson'? Câu trả lời dưới đây là chính xác. –

+0

Không có @ ToJson ở đó, tôi vừa bỏ qua tất cả các mã mà tôi nghĩ là không quan trọng ví dụ – miszmaniac

+0

hm, sau đó, điều này trông giống như câu trả lời. bạn có làm cho nó hoạt động không? –

Trả lời

0

Hóa ra là mã của tôi đã thực sự đúng ngay từ đầu!

Vấn đề là với tuyên bố lĩnh vực bên trong dữ liệu Class:

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

Nó hoạt động với val, và không làm việc với var! Kotlin bằng cách nào đó tạo mã khác dưới đây. Đây là mã cuối cùng của tôi cho phần này của mô hình:

@JvmSuppressWildcards var layers: List<Layer> 
4

Vâng, bạn có thể tạo một bộ chuyển đổi kiểu tùy chỉnh để phân tích json theo layerType như thế này:

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

Ở đây tôi có thực hiện một số thay đổi để lớp dữ liệu của bạn cho rõ ràng (một tài sản thêm cho mỗi Layer loại, ví dụ như shape cho ShapeLayer):

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 
} 

kiểm tra mã:

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) 

Output:

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

Edit: Bạn có thể thêm adapter như bình thường khi bạn đang cố gắng để phân tích đối tượng khác có chứa Layer. Giả sử bạn có một đối tượng như thế này:

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

Thử nghiệm:

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

Câu trả lời hay. Thích cái này. –

+0

OMG đó là câu trả lời đầy đủ :) Cảm ơn bạn rất nhiều! Đáng buồn thay, điều đó không hiệu quả đối với tôi :) Tôi trích xuất json của tôi để tạo mảng lớp tại gốc của json, và nó thực sự hoạt động, nhưng vấn đề ở một nơi khác: Json của tôi trông giống như: {"layers" : [... danh sách này ...]} Tôi không biết cách đính kèm bộ điều hợp danh sách Đã nhập này vào trình tạo? – miszmaniac

+0

@miszmaniac Tôi đã cập nhật câu trả lời cho trường hợp này. – BakaWaii