使用 Moshi 序列化密封类

and*_*rew 10 android 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))
}
Run Code Online (Sandbox Code Playgroud)

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

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

    ....
}
Run Code Online (Sandbox Code Playgroud)

最终我希望生成如下所示的 JSON:

{
    "type" : "cat",
    "properties" : {
        "remainingLives" : 6
    }
}
Run Code Online (Sandbox Code Playgroud)
{
    "type" : "dog",
    "properties" : {
        "isGoodBoy" : true
    }
}
Run Code Online (Sandbox Code Playgroud)

我很高兴必须使用自定义适配器来编写每种类型的名称,但我需要一个解决方案来自动序列化每种类型的属性,而不必手动编写它们。

Luc*_*ano 12

这可以通过PolymorphicJsonAdapterFactory在 json 中包含一个额外的属性来指定类型来完成。

例如:

这个 JSON

{
  "animals": [
    { 
        "type": "dog",
        "isGoodBoy": true
    },
    {
        "type": "cat",
        "remainingLives": 9
    }    
  ]
}
Run Code Online (Sandbox Code Playgroud)

可以映射到以下类

sealed class Animal {
    @JsonClass(generateAdapter = true)
    data class Dog(val isGoodBoy: Boolean) : Animal()

    @JsonClass(generateAdapter = true)
    data class Cat(val remainingLives: Int) : Animal()

    object Unknown : Animal()
}
Run Code Online (Sandbox Code Playgroud)

使用以下 Moshi 配置

Moshi.Builder()
    .add(
        PolymorphicJsonAdapterFactory.of(Animal::class.java, "type")
            .withSubtype(Animal.Dog::class.java, "dog")
            .withSubtype(Animal.Cat::class.java, "cat")
            .withDefaultValue(Animal.Unknown)
    )
Run Code Online (Sandbox Code Playgroud)