Сериализация закрытого класса с Моши - PullRequest
0 голосов
/ 01 мая 2019

Следующее создаст исключение 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 голосов
/ 01 мая 2019

Я решил эту проблему, создав Фабрику, включающий класс и перечисление, которые могут предоставить классы для каждого типа элементов. Однако это кажется довольно неуклюжим, и я хотел бы более прямого решения.

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

0 голосов
/ 01 мая 2019

Я думаю, вам нужен полиморфный адаптер, чтобы добиться этого, для чего нужен артефакт moshi-adapters.Это позволит сериализовать запечатанные классы с различными свойствами.Подробнее в этой статье здесь: https://proandroiddev.com/moshi-polymorphic-adapter-is-d25deebbd7c5

...