Moshi 适配器跳过 List<T> 中的坏对象

Posted

技术标签:

【中文标题】Moshi 适配器跳过 List<T> 中的坏对象【英文标题】:Moshi adapter to skip bad objects in the List<T> 【发布时间】:2019-06-06 07:35:55 【问题描述】:

我使用 Moshi,我需要用一个有问题的后端来解决我的问题。有时,当我请求对象列表时,其中一些不包含必填字段。当然,我可以捕捉和处理JsonDataException,但我想跳过这些对象。我如何使用 Moshi 做到这一点?

更新

我有几个模型来完成我的任务

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

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

和错误的 JSON


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

如您所见,第二个对象没有强制的 name 字段,我想通过 Moshi 适配器跳过它。

【问题讨论】:

发布有关如何设置 Moshi 适配器的代码。 @SharpMobileCode 我没有适配器的代码。我只是想写它=)但我可以用模型显示代码。 【参考方案1】:

解决方案中有一个陷阱,只有在失败后才会捕获和忽略。如果您的元素适配器在发生错误后停止读取,例如,读取器可能正在读取嵌套对象,然后将在错误的位置调用下一个 hasNext 调用。

正如 Jesse 所说,您可以查看并跳过整个值。

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()
  

【讨论】:

此实现在调用 create 时会因PolymorphicJsonAdapterFactory 而崩溃。 com.squareup.moshi.adapters.PolymorphicJsonAdapterFactory.create(PolymorphicJsonAdapterFactory.java:216)【参考方案2】:

我好像找到了答案

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

谢谢“其他主题的人”=)

【讨论】:

考虑使用 JsonReader.peekJson() 来获取当前不消耗的 JsonReader 的副本。即使嵌套对象格式错误,您也可以使用 skip() 跳过完整值。 我遇到了一个问题,即它如何与 Kotlin 泛型结合起来。我不断得到的例外是:java.lang.IllegalArgumentException: No JsonAdapter for E (with no annotations)。如果有人有解决方法,将不胜感激!另外,我觉得 Moshi 应该开箱即用地提供这个功能作为注释或其他东西。它在许多其他解析器中很常见。【参考方案3】:

您可以在这里找到可行的解决方案:

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

修复愉快:)

【讨论】:

以上是关于Moshi 适配器跳过 List<T> 中的坏对象的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Moshi 中跳过 JSON 属性?

Moshi适配器用于注释模型

Moshi:解析单个对象或对象列表(kotlin)

如何在 moshi (kotlin) 中解析 LinkedHashMap

Android--ListView与数据绑定(Xamarin)

STL中的vector 和list