Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 718 Vote(s) - 3.57 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Moshi + Kotlin + SealedClass

#1
Is there a way of deserializing json using

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 is just some enum which can be used to distinguish which type should this object have.

I thought I could add Adapter this way:

class LayerAdapter{
@FromJson
fun fromJson(layerJson: LayerJson): Layer {
return when (layerJson.layerType) {
LayerType.SHAPE -> PreCompLayer()
LayerType.SOLID -> SolidLayer()
LayerType.Text -> TextLayer()
}
}
}
Where LayerJson would be object which has every possible field of all LayerTypes.

Now the problem is:
> Cannot serialize abstract class com.example.models.layers.Layer

I could try to use interface, but I don't think it would be correct to use empty interface in this.
Reply

#2
Yes, you can create a custom type adapter to parse json according to the `layerType` like this:

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

Here I have make some changes to your data class for clarity (an extra property to each of the `Layer` type, e.g. `shape` for `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
}

Testing code:

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:
You can add the adapter as usual when you are trying to parse other object which contain `Layer`. Suppose you have an object like this:

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

Testing code:

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

#3
It turned out that my code was actually correct from beginning!

Problem was with field declaration inside data Class:

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

It works with val, and doesn't work with var!
Kotlin somehow creates different code down below.
This is my final code for this part of model:

@JvmSuppressWildcards var layers: List<Layer>


Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through