使用Moshi序列化密封类

时间:2019-05-01 09:18:49

标签: json kotlin moshi

由于您“无法序列化抽象类”,因此以下内容将产生IllegalArgumentException

sealed class Animal {
    data class Dog(val isGoodBoy: Boolean) : Animal()
    data class Cat(val remainingLives: Int) : Animal()
}

private val moshi = Moshi.Builder()
    .build()

@Test
fun test() {
    val animal: Animal = Animal.Dog(true)
    println(moshi.adapter(Animal::class.java).toJson(animal))
}

我尝试使用自定义适配器解决此问题,但是我能找出的唯一解决方案是为每个子类显式编写所有属性名称。例如:

class AnimalAdapter {
    @ToJson
    fun toJson(jsonWriter: JsonWriter, animal: Animal) {
        jsonWriter.beginObject()
        jsonWriter.name("type")
        when (animal) {
            is Animal.Dog -> jsonWriter.value("dog")
            is Animal.Cat -> jsonWriter.value("cat")
        }

        jsonWriter.name("properties").beginObject()
        when (animal) {
            is Animal.Dog -> jsonWriter.name("isGoodBoy").value(animal.isGoodBoy)
            is Animal.Cat -> jsonWriter.name("remainingLives").value(animal.remainingLives)
        }
        jsonWriter.endObject().endObject()
    }

    ....
}

最终我正在寻找生成如下所示的JSON:

{
    "type" : "cat",
    "properties" : {
        "remainingLives" : 6
    }
}
{
    "type" : "dog",
    "properties" : {
        "isGoodBoy" : true
    }
}

我很高兴使用自定义适配器编写每种类型的名称,但是我需要一种解决方案,该解决方案可以自动序列化每种类型的属性,而不必手动编写所有属性。

2 个答案:

答案 0 :(得分:0)

我认为您需要多态适配器才能实现此目的,这需要moshi-adapters工件。这将启用具有不同属性的密封类的序列化。本文中的更多详细信息:https://proandroiddev.com/moshi-polymorphic-adapter-is-d25deebbd7c5

答案 1 :(得分:0)

我已经通过创建一个Factory,一个封闭的类和一个可以为每种商品类型提供类的枚举来解决此问题。但是,这感觉很笨拙,我希望有一个更直接的解决方案。

data class AnimalObject(val type: AnimalType, val properties: Animal)

enum class AnimalType(val derivedClass: Class<out Animal>) {
    DOG(Animal.Dog::class.java),
    CAT(Animal.Cat::class.java)
}

class AnimalFactory : JsonAdapter.Factory {
    override fun create(type: Type, annotations: MutableSet<out Annotation>, moshi: Moshi): JsonAdapter<AnimalObject>? {
        if (!Types.getRawType(type).isAssignableFrom(AnimalObject::class.java)) {
            return null
        }

        return object : JsonAdapter<AnimalObject>() {
            private val animalTypeAdapter = moshi.adapter<AnimalType>(AnimalType::class.java)

            override fun fromJson(reader: JsonReader): AnimalObject? {
                TODO()
            }

            override fun toJson(writer: JsonWriter, value: AnimalObject?) {
                writer.beginObject()
                writer.name("type")
                animalTypeAdapter.toJson(writer, value!!.type)
                writer.name("properties")
                moshi.adapter<Animal>(value.type.derivedClass).toJson(writer, value.properties)
                writer.endObject()
            }
        }
    }
}

答案来自:github.com/square/moshi/issues/813