Moshi + Kotlin + SealedClass

21

有没有一种方法可以使用 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只是一个枚举类型,可以用来区分该对象应该具有的类型。

我认为我可以这样添加适配器:

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

我可以尝试使用接口,但我认为在这种情况下使用空接口是不正确的。


我认为你只是缺少了@ToJson方法?下面的答案是正确的。 - Eric Cochran
没有@ToJson,我跳过了所有我认为不重要的代码,以示例为例。 - miszmaniac
嗯,那么,这看起来和答案一样。你搞定了吗? - Eric Cochran
看我的回答,问题出在别的地方! - miszmaniac
2个回答

19

是的,您可以创建一个自定义类型适配器来根据layerType解析JSON,方法如下:

是的,您可以创建一个自定义类型适配器来根据layerType解析JSON,方法如下:

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类型都添加了一个额外的属性,例如ShapeLayershape):

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)

5
很棒的回答。喜欢这个。 - Jesse Wilson
哇,这是一个详尽的回答 :) 非常感谢! 可惜对我没用 :( 我提取了我的JSON以使层数组位于JSON根目录下,它确实起作用,但问题出在其他地方: 我的JSON看起来像这样: {"layers": [... 这个列表 ...]} 我不知道如何将这个类型化列表适配器附加到构建器上? - miszmaniac

1
事实证明,我的代码一开始就是正确的!问题出在数据类内的字段声明:
data class LayerContainer(var/val layers: List<Layer>)

它可以使用val,但无法使用var! 某种程度上,Kotlin会在下面创建不同的代码。 这是我针对模型的最终代码:

@JvmSuppressWildcards var layers: List<Layer>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接