I have a polymorphic list of contents sent by the backend. I use PolymorphicJsonAdapterFactory to parse it to Kotlin objects. It all works fine. When the backend sends an element with an unknown type there is no problem I apply a default value.
The issue comes when the backend sends a wrong element (required attribute missing) with a recognized type. In this case, moshi stops and throws a JsonDataException. I would like to ignore these elements.
I saw this thread but it concerns a list of just one type.
The only solution I could find so far is making EVERY field on the Kotlin data classes optional and filter out later. But it's not ideal.
Let's assume I have this :
enum class Type {
FISH,
BIRD
}
sealed class Animal(val type: Type) {
data class Fish(
val requiredField : String,
): Animal(Type.FISH)
data class Bird(
val requiredField : String,
): Animal(Type.BIRD)
}
And I receive a list of Animal.
So I'll have :
PolymorphicJsonAdapterFactory.of(Animal::class.java, "type")
.withSubtype(Fish::class.java, Type.FISH)
.withSubtype(Bird::class.java, Type.BIRD)
.withSubtype(Input::class.java, ActionType.input.name))
.withDefaultValue(UnknownEntity())
This will handle this json perfectly :
{
"animals": [
{
"type": "FISH",
"requiredField": "blablabla",
},
{
"type": "ANIMAL",
"requiredField": "blablabla"
},
{
"type": "Nothing",
}
]
}
But not this one because fish required field is missing are missing :
{
"animals": [
{
"type": "FISH",
},
{
"type": "ANIMAL",
"requiredField": "blablabla"
},
]
}
Do you have a solution for this ? Seems like a pretty normal usecase
I used my SkipBadElementsListAdapter
from your linked thread, and it seems to do what you want. Correct me if I misunderstood (preferably with a demonstrating test case).
fun main() {
val animalJsonAdapterFactory = PolymorphicJsonAdapterFactory.of(Animal::class.java, "type")
.withSubtype(Animal.Fish::class.java, Type.FISH.name)
.withSubtype(Animal.Bird::class.java, Type.BIRD.name)
val moshi = Moshi.Builder()
.add(SkipBadElementsListAdapter.Factory)
.add(animalJsonAdapterFactory)
.build()
val animalsJsonAdapter =
moshi.adapter<List<Animal>>(Types.newParameterizedType(List::class.java, Animal::class.java))
println(animalsJsonAdapter.fromJson(encoded))
}
enum class Type {
FISH,
BIRD
}
sealed class Animal(val type: Type) {
@JsonClass(generateAdapter = true)
data class Fish(
val requiredField: String,
) : Animal(Type.FISH)
@JsonClass(generateAdapter = true)
data class Bird(
val requiredField: String,
) : Animal(Type.BIRD)
}
val encoded = """
[
{
"type": "FISH",
"requiredField": "blablabla"
},
{
"type": "BIRD",
"requiredField": "blablabla"
},
{
"type": "BIRD"
}
]
""".trimIndent()
This prints [Fish(requiredField=blablabla), Bird(requiredField=blablabla)]
. It ignores the bad element, as it seems you want it to do.