问题
如何使用Warning从API中解析单个Warning对象或Warning对象列表(List<Warning>)
作为一个单一警告的响应:
{
"warnings": {...}
}作为警告列表的响应:
{
"warnings": [{...}, {...}]
}试错
试图为自动生成的莫施适配器安装鞋帮。试图建立在上面,但失败了。
解决方案
带工厂的广义方法
我试图将适配器Eric从Java翻译到Kotlin,因为我意识到更通用的方法更好,正如Eric在他的答复中所指出的。
一旦成功,我将修改这篇文章,使它更容易理解。现在有点乱,对不起。
编辑:我最后使用了在另一个线程中建议的解决方案 (翻译成Kotlin)。
工厂适配器
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)。
@Retention(RUNTIME)
@Target(FIELD)
@JsonQualifier
annotation class SingleToArray用法
注释要确保使用@SingleToArray解析为列表的字段。
data class Alert(
@SingleToArray
@Json(name = "alert")
val alert: List<Warning>
)并将适配器工厂添加到莫施实例中:
val moshi = Moshi.Builder()
.add(SingleToArrayAdapter.factory)
.build()参考文献
发布于 2018-11-19 02:37:25
发布于 2022-04-28 11:26:42
另一种解决办法
基于Eric的Java版本解决方案。
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")
}
}
}https://stackoverflow.com/questions/53344033
复制相似问题