Moshi adapter to ignore bad elements from a polymorphic list

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.

Example

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

Upvotes: 1

Views: 663

Answers (1)

Eric Cochran
Eric Cochran

Reputation: 8574

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.

Upvotes: 0

Related Questions