Moshi + Kotlin + SealedClass

Posted

技术标签:

【中文标题】Moshi + Kotlin + SealedClass【英文标题】: 【发布时间】:2018-03-02 20:53:40 【问题描述】:

有没有办法使用

反序列化 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 将是具有所有 LayerType 的每个可能字段的对象。

现在的问题是:

无法序列化抽象类 com.example.models.layers.Layer

我可以尝试使用接口,但我认为在此使用空接口是不正确的。

【问题讨论】:

我想你只是错过了@ToJson 方法?下面的答案是正确的。 没有@ToJson,我只是跳过了所有我认为不重要的代码 嗯,那么,这看起来和答案一样。你搞定了吗? 看我的回答,问题出在其他地方! 【参考方案1】:

是的,您可以创建一个自定义类型适配器来根据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 类型的额外属性,例如shape 用于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)

【讨论】:

很好的答案。喜欢这个。 天哪,这是一个详尽的答案:) 非常感谢!可悲的是,这对我不起作用:) 我提取了我的 json 以在 json 的根目录创建层数组,它确实有效,但问题出在其他地方:我的 json 看起来像这样:“layers”:[.. . 这个列表...]我不知道如何将这个类型列表适配器附加到构建器?【参考方案2】:

原来我的代码从一开始就是正确的!

问题在于数据类中的字段声明:

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

它适用于 val,不适用于 var! Kotlin 以某种方式在下面创建了不同的代码。 这是我对这部分模型的最终代码:

@JvmSuppressWildcards var layers: List<Layer>

【讨论】:

以上是关于Moshi + Kotlin + SealedClass的主要内容,如果未能解决你的问题,请参考以下文章

Moshi 的 Kotlin 代码生成器有啥用?

Moshi 1.9.x 无法序列化 Kotlin 类型

如何在 moshi (kotlin) 中解析 LinkedHashMap

Moshi:解析单个对象或对象列表(kotlin)

Kotlin Retrofit Moshi:不返回任何内容

(Kotlin 中的 Moshi)@Json 与 @field:Json