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

Posted

技术标签:

【中文标题】Moshi:解析单个对象或对象列表(kotlin)【英文标题】:Moshi: Parse single object or list of objects (kotlin) 【发布时间】:2019-04-20 00:09:36 【问题描述】:

我正在构建一个应用程序,该应用程序可以从我无法控制的 API 中获取 Warning 对象,他们也不想删除我正在尝试解决的这种行为。

我希望能够解析单个 Warning 对象或 List<Warning> 对象。 (因为如果只有 1 个或 > 1 个对象列表,API 会返回 1 个对象。)

问题

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

我得到的数据看起来像这样:


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

或者像这样:


  # Warning
  "warnings": ...

我已经阅读并找到了一些关于这个主题的资料,但没有示例,我不知道如何继续..所以任何指针都将受到高度赞赏。

尝试

我最初试图硬塞一个自动生成的 moshi 适配器,以为我可以在它之上构建,但我不知道发生了什么,所以它并没有真正引导我到任何地方。但是我还是包含了生成的代码,以防它仍然对任务有用。

编辑:这是我目前所拥有的,虽然我对需要一个 Moshi 实例才能工作这一事实不太满意。

解决方案

使用工厂的通用方法

我试图移植 Eric 写给 kotlin 的适配器 因为我已经意识到更通用的方法更好 埃里克在他的回复中指出。一旦这个工作正常,我将重写这篇文章的部分内容以使其更容易理解,现在有点混乱,我为此道歉。

编辑:我最终使用了另一个线程中建议的 solution Eric,但移植到了 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)
        
    

预选赛

注意:我必须添加Target(FIELD)

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

用法

@SingleToArray注释您要确保解析为列表的字段。

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

别忘了将适配器添加到您的 moshi 实例中:

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

相关问题/主题:

Parse JSON key that is either object or array of object Moshi Determine if JSON is array or single object https://github.com/square/moshi

【问题讨论】:

【参考方案1】:

如果只有 1 个或 > 1 个对象列表,则 API 返回 1 个对象。

创建一个适配器,以查看您是否首先获得了一个数组。 Here 正是您想要的。它包含一个限定符,因此您只能将其应用于可能对单个项目具有此行为的列表。 @SingleToArray List&lt;Warning&gt;.

还有另一个处理多种格式的示例here 供进一步阅读。

【讨论】:

埃里克,谢谢你的回复——你发布的链接确实是我正在寻找的,我确实得到了“类似”的东西,但感觉我仍然缺少一些东西。我已经用我正在使用的当前适配器代码更新了我的答案,它可以工作,但感觉很脏..你觉得呢? 使用 JsonAdapter.Factory,不需要传入 Moshi 实例。 我试图使用工厂来避免 moshi 实例,但不知何故这完全改变了适配器的行为,但我认为我很接近,我可能只是错过了一些微不足道的东西。我同时添加了代码供参考。 我最终确实让它工作了,忘记在构建之间清理项目。谢谢你,也接受了你的回答,因为这是我最终使用的:)

以上是关于Moshi:解析单个对象或对象列表(kotlin)的主要内容,如果未能解决你的问题,请参考以下文章

如何使用带有 Kotlin 的 Room 和 moshi 持久化带有 JSON 数组的 JSON 对象

如何在 moshi (kotlin) 中解析 LinkedHashMap

使用 Moshi 将字符串日期从 json 转换为 Date 对象

Kotlin Retrofit Moshi:不返回任何内容

如何使用 Kotlin 序列化解析 JSON 对象列表中的第一个属性?

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