Moshi + Kotlin + SealedClass

Есть ли способ десериализации json, используя

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 – это просто перечисление, которое можно использовать для определения того, какой тип должен иметь этот объект.

Я думал, что могу добавить Adapter следующим образом:

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

Где LayerJson будет объектом, который имеет все возможные области всех типов LayerTypes.

Теперь проблема заключается в следующем:

Невозможно сериализовать абстрактный класс com.example.models.layers.Layer

Я мог бы попробовать использовать интерфейс, но я не думаю, что было бы правильно использовать пустой интерфейс в этом.

Да, вы можете создать адаптер настраиваемого типа для синтаксического анализа json в соответствии с layerType следующим образом:

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

Здесь я сделал некоторые изменения для вашего класса данных для ясности (дополнительное свойство для каждого типа Layer , например, для 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 } 

Код тестирования:

 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) 

Вывод:

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

Изменить: вы можете добавить адаптер, как обычно, когда вы пытаетесь проанализировать другой объект, содержащий Layer . Предположим, у вас есть такой объект:

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

Код тестирования:

 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) 

Оказалось, что мой код был действительно правильным с самого начала!

Проблема заключалась в декларации поля внутри данных Класс:

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

Он работает с val и не работает с var! Котлин каким-то образом создает другой код ниже. Это мой последний код для этой части модели:

 @JvmSuppressWildcards var layers: List<Layer> 
Intereting Posts