Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

398
Views
Moshi adapter to skip bad objects in the List<T>

I use Moshi and I need to solve my problem with a buggy backend. Sometimes, when I request a list of objects, some of them don't contain mandatory fields. Of course, I can catch and process JsonDataException, but I want to skip these objects. How can I do it with Moshi?

Update

I have a couple of models for my task

@JsonClass(generateAdapter = true)
data class User(
        val name: String,
        val age: Int?
)

@JsonClass(generateAdapter = true)
data class UserList(val list: List<User>)

and buggy JSON

{
  "list": [
    {
      "name": "John",
      "age": 20
    },
    {
      "age": 18
    },
    {
      "name": "Jane",
      "age": 21
    }
  ]
}

as you can see, the second object has no mandatory name field and I want to skip it via Moshi adapter.

over 4 years ago · Santiago Trujillo
3 answers
Answer question

0

There's a gotcha in the solution that only catches and ignores after failure. If your element adapter stopped reading after an error, the reader might be in the middle of reading a nested object, for example, and then the next hasNext call will be called in the wrong place.

As Jesse mentioned, you can peek and skip the entire value.

class SkipBadElementsListAdapter(private val elementAdapter: JsonAdapter<Any?>) :
    JsonAdapter<List<Any?>>() {
  object Factory : JsonAdapter.Factory {
    override fun create(type: Type, annotations: Set<Annotation>, moshi: Moshi): JsonAdapter<*>? {
      if (annotations.isNotEmpty() || Types.getRawType(type) != List::class.java) {
        return null
      }
      val elementType = Types.collectionElementType(type, List::class.java)
      val elementAdapter = moshi.adapter<Any?>(elementType)
      return SkipBadElementsListAdapter(elementAdapter)
    }
  }

  override fun fromJson(reader: JsonReader): List<Any?>? {
    val result = mutableListOf<Any?>()
    reader.beginArray()
    while (reader.hasNext()) {
      try {
        val peeked = reader.peekJson()
        result += elementAdapter.fromJson(peeked)
      } catch (ignored: JsonDataException) {
      }
      reader.skipValue()
    }
    reader.endArray()
    return result

  }

  override fun toJson(writer: JsonWriter, value: List<Any?>?) {
    if (value == null) {
      throw NullPointerException("value was null! Wrap in .nullSafe() to write nullable values.")
    }
    writer.beginArray()
    for (i in value.indices) {
      elementAdapter.toJson(writer, value[i])
    }
    writer.endArray()
  }
}
over 4 years ago · Santiago Trujillo Report

0

It seems I've found the answer

class SkipBadListObjectsAdapterFactory : JsonAdapter.Factory {
    override fun create(type: Type, annotations: MutableSet<out Annotation>, moshi: Moshi): JsonAdapter<*>? {
        return if (annotations.isEmpty() && Types.getRawType(type) == List::class.java) {
            val elementType = Types.collectionElementType(type, List::class.java)
            val elementAdapter = moshi.adapter<Any>(elementType)

            SkipBadListObjectsAdapter(elementAdapter)
        } else {
            null
        }
    }

    private class SkipBadListObjectsAdapter<T : Any>(private val elementAdapter: JsonAdapter<T>) :
        JsonAdapter<List<T>>() {
        override fun fromJson(reader: JsonReader): List<T>? {
            val goodObjectsList = mutableListOf<T>()

            reader.beginArray()

            while (reader.hasNext()) {
                try {
                    elementAdapter.fromJson(reader)?.let(goodObjectsList::add)
                } catch (e: JsonDataException) {
                    // Skip bad element ;)
                }
            }

            reader.endArray()

            return goodObjectsList

        }

        override fun toJson(writer: JsonWriter, value: List<T>?) {
            throw UnsupportedOperationException("SkipBadListObjectsAdapter is only used to deserialize objects")
        }
    }
}

Thank you "guys from the other topics" =)

over 4 years ago · Santiago Trujillo Report

0

You can find a working solution here:

https://github.com/square/moshi/issues/1288

Happy fixing :)

over 4 years ago · Santiago Trujillo Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!