首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Moshi:解析单个对象或对象列表(kotlin)

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

Stack Overflow用户
提问于 2018-11-16 19:12:41
回答 2查看 7.1K关注 0票数 14

问题

如何使用Warning从API中解析单个Warning对象或Warning对象列表(List<Warning>)

作为一个单一警告的响应:

代码语言:javascript
复制
{
  "warnings": {...}
}

作为警告列表的响应:

代码语言:javascript
复制
{
  "warnings": [{...}, {...}]
}

试错

试图为自动生成的莫施适配器安装鞋帮。试图建立在上面,但失败了。

解决方案

带工厂的广义方法

我试图将适配器Eric从Java翻译到Kotlin,因为我意识到更通用的方法更好,正如Eric在他的答复中所指出的。

一旦成功,我将修改这篇文章,使它更容易理解。现在有点乱,对不起。

编辑:我最后使用了在另一个线程中建议的解决方案 (翻译成Kotlin)。

工厂适配器

代码语言:javascript
复制
package org.domain.name

import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.JsonQualifier
import com.squareup.moshi.JsonReader
import com.squareup.moshi.JsonWriter
import com.squareup.moshi.Moshi
import com.squareup.moshi.Types
import java.util.Collections
import java.lang.reflect.Type
import kotlin.annotation.AnnotationRetention.RUNTIME
import kotlin.annotation.AnnotationTarget.FIELD

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

    companion object {
        val factory = 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) ?: return null
            if (Types.getRawType(type) !== List::class.java) throw IllegalArgumentException("Only List can 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)

代码语言:javascript
复制
@Retention(RUNTIME)
@Target(FIELD)
@JsonQualifier
annotation class SingleToArray

用法

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

代码语言:javascript
复制
data class Alert(
    @SingleToArray
    @Json(name = "alert")
    val alert: List<Warning>
)

并将适配器工厂添加到莫施实例中:

代码语言:javascript
复制
val moshi = Moshi.Builder()
    .add(SingleToArrayAdapter.factory)
    .build()

参考文献

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-11-19 02:37:25

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

创建一个适配器,查看是否首先得到一个数组。这里正是你想要的。它包括一个限定符,因此您只能将其应用于可能对单个项具有此行为的列表。@SingleToArray List<Warning>

还有一个处理多格式这里的示例,以供进一步阅读。

票数 4
EN

Stack Overflow用户

发布于 2022-04-28 11:26:42

另一种解决办法

基于Eric的Java版本解决方案

代码语言:javascript
复制
import com.squareup.moshi.JsonAdapter
import com.squareup.moshi.JsonAdapter.Factory
import com.squareup.moshi.JsonQualifier
import com.squareup.moshi.JsonReader
import com.squareup.moshi.JsonReader.Token.BEGIN_ARRAY
import com.squareup.moshi.JsonWriter
import com.squareup.moshi.Moshi
import com.squareup.moshi.Types
import java.lang.reflect.Type
import java.util.Collections.singletonList
import java.util.Collections.emptyList
import kotlin.annotation.AnnotationRetention.RUNTIME
import kotlin.annotation.AnnotationTarget.PROPERTY
import org.junit.Assert.assertEquals

@Retention(RUNTIME)
@Target(PROPERTY)
@JsonQualifier
annotation class SingleOrList

object SingleOrListAdapterFactory : Factory {
    override fun create(
        type: Type,
        annotations: Set<Annotation>,
        moshi: Moshi
    ): JsonAdapter<*>? {
        val delegateAnnotations = Types.nextAnnotations(annotations, SingleOrList::class.java)
            ?: return null
        if (Types.getRawType(type) !== List::class.java) {
            throw IllegalArgumentException("@SingleOrList requires the type to be List. Found this type: $type")
        }
        val elementType = Types.collectionElementType(type, List::class.java)
        val delegateAdapter: JsonAdapter<List<Any?>?> = moshi.adapter(type, delegateAnnotations)
        val singleElementAdapter: JsonAdapter<Any?> = moshi.adapter(elementType)
        return object : JsonAdapter<List<Any?>?>() {
            override fun fromJson(reader: JsonReader): List<Any?>? =
                if (reader.peek() !== BEGIN_ARRAY)
                    singletonList(singleElementAdapter.fromJson(reader))
                else
                    delegateAdapter.fromJson(reader)
            override fun toJson(writer: JsonWriter, value: List<Any?>?) {
                if (value == null) return
                if (value.size == 1)
                    singleElementAdapter.toJson(writer, value[0])
                else
                    delegateAdapter.toJson(writer, value)
            }
        }
    }
}

class TheUnitTest {

    @JsonClass(generateAdapter = true)
    internal data class MockModel(
        @SingleOrList
        val thekey: List<String>
    )

    @Test
    @Throws(Exception::class)
    fun testAdapter() {
        val moshi = Moshi.Builder().add(SingleOrListAdapterFactory).build()
        val adapter: JsonAdapter<List<String>> = moshi.adapter(
            Types.newParameterizedType(
                List::class.java,
                String::class.java),
            SingleOrList::class.java
        )
        assertEquals(adapter.fromJson("[\"Alice\",\"Bob\"]"), listOf("Alice", "Bob"))
        assertEquals(adapter.toJson(listOf("Bob", "Alice")), "[\"Bob\",\"Alice\"]")

        assertEquals(adapter.fromJson("\"Alice\""), singletonList("Alice"))
        assertEquals(adapter.toJson(singletonList("Alice")), "\"Alice\"")

        assertEquals(adapter.fromJson("[]"), emptyList<String>())
        assertEquals(adapter.toJson(emptyList()), "[]")
    }

    @Test
    fun testDataClassUsage() {
        val j1 = """
            {
                "thekey": "value1"
            }
        """.trimIndent()
        val j2 = """
            {
                "thekey": [
                    "value1",
                    "value2",
                    "value3"
                ]
            }
        """.trimIndent()

        val o1 = MockModel::class.java.fromJson(j1, moshi)?.thekey
        val o2 = MockModel::class.java.fromJson(j2, moshi)?.thekey

        if (o1 != null && o2 != null) {
            assertEquals(o1.size, 1)
            assertEquals(o1[0], "value1")
            assertEquals(o2.size, 3)
            assertEquals(o2[0], "value1")
            assertEquals(o2[1], "value2")
            assertEquals(o2[2], "value3")
        }
    }
}
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/53344033

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档