我正在构建一个应用程序,它可以从一个API中获取Warning个对象,我不控制这个API,他们也不想删除我正在try 解决的行为.

I would like to be able to parse either a single Warning object or a List<Warning> objects. (Because the API returns either 1 object if there is only 1 or > 1 a list of objects.)

Question

如何将对象直接解析为列表或使用Moshi解析列表?

The data I get looks either like this:

{
  # List<Warning>
  "warnings": [{...}, {...}]
}

或者像这样:

{
  # Warning
  "warnings": {...}
}

I have read and found some sources on this topic, but no examples and I am not sure how to proceed.. so any pointers would be highly appreciated.

Attempt

起初,我试图用鞋钉固定一个自动生成的moshi适配器,以为我可以在上面构建它,但我不知道发生了什么,所以它并没有真正把我带到任何地方.但我还是包含了生成的代码,以防它对任务仍然有用罢工>

Edit: This is what I currently have, though im not very pleased by the fact it takes an instance of Moshi to work.

Solution

Generalised approach with a Factory

I tried to port adapter which Eric wrote to kotlin since I since has realised that a more general approach is better much like Eric points out in his reply. As soon a this is working, I will re-write parts of this post to make it more understandable, its a bit messy now I apologize for that.

EDIT: I ended up using the solution Eric suggested in another thread but ported to Kotlin.

带工厂的适配器

class SingleToArrayAdapter(
    val delegateAdapter: JsonAdapter<List<Any>>,
    val elementAdapter: JsonAdapter<Any>
) : JsonAdapter<Any>() {

    companion object {
        val INSTANCE = SingleToArrayAdapterFactory()
    }

    override fun fromJson(reader: JsonReader): Any? =
        if (reader.peek() != JsonReader.Token.BEGIN_ARRAY) {
            Collections.singletonList(elementAdapter.fromJson(reader))
        } else delegateAdapter.fromJson(reader)

    override fun toJson(writer: JsonWriter, value: Any?) =
        throw UnsupportedOperationException("SingleToArrayAdapter is only used to deserialize objects")

    class SingleToArrayAdapterFactory : JsonAdapter.Factory {
        override fun create(type: Type, annotations: Set<Annotation>, moshi: Moshi): JsonAdapter<Any>? {
            val delegateAnnotations = Types.nextAnnotations(annotations, SingleToArray::class.java)
            if (delegateAnnotations == null) return null
            if (Types.getRawType(type) != List::class.java) throw IllegalArgumentException("Only lists may be annotated with @SingleToArray. Found: $type")
            val elementType = Types.collectionElementType(type, List::class.java)
            val delegateAdapter: JsonAdapter<List<Any>> = moshi.adapter(type, delegateAnnotations)
            val elementAdapter: JsonAdapter<Any> = moshi.adapter(elementType)

            return SingleToArrayAdapter(delegateAdapter, elementAdapter)
        }
    }
}

Qualifier

Note: I had to add the Target(FIELD).

@Retention(RUNTIME)
@Target(FIELD)
@JsonQualifier
annotation class SingleToArray

Usage

Annotate a field you want to make sure is parsed as a list with @SingleToArray.

data class Alert(
    @SingleToArray
    @Json(name = "alert")
    val alert: List<Warning>
)

and dont forget to add the adapter to your moshi instance:

val moshi = Moshi.Builder()
            .add(SingleToArrayAdapter.INSTANCE)
            .build()

相关问题/主题:

推荐答案

the API returns either 1 object if there is only 1 or > 1 a list of objects.

创建一个适配器,查看是否首先获得array.

There is another example of dealing with multiple formats here for further reading.

Kotlin相关问答推荐

如何在Kotlin中模拟www.example.com()?

"Kotlin中的表达式

T和T有什么区别:任何>

Java/Kotlin中类似Rust般的注释编译?

垂直滚动条下拉菜单的桌面组合

Kotlin 中的密封和内部有什么区别?

内容更改后的 var 重新计算

为什么没有remember 的 mutableStateOf 有时会起作用?

为什么 IntelliJ Idea 无法识别我的 Spek 测试?

Kotlin 1.2.21 + SimpleXml 2.3.0 - consume List error (must mark set get method)

将 Completable 转换为 Single 的规范方法?

Kotlin - 覆盖方法中的 IllegalArgumentException

Kotlin DataBinding 将静态函数传递到布局 xml

将协同路由调用放在存储库或ViewModel中哪个更好?

Android 与 Kotlin - 如何使用 HttpUrlConnection

Kotlin lambda 语法混淆

Kotlin 警告:Conditional branch result of type ... is implicity cast of Any?

通过在 kotlin-gradle 中使用子项目Unresolved reference: implementation

比较Kotlin的NaN

Kotlin 是否支持部分应用程序?