Search code examples
androidjsonkotlinadaptermoshi

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.


Solution

  • 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" =)