Я создаю приложение, которое может извлекать объект Warning
из API, который я не контролирую, и при этом они не хотят удалять это поведение, с которым я пытаюсь обойти.
Я хотел бы иметь возможность анализировать либо один объект Warning
, либо объект List<Warning>
. (Поскольку API возвращает либо 1 объект, если имеется только 1, либо> 1 список объектов.)
Вопрос
Как я могу проанализировать объект как список напрямую или проанализировать его с помощью Moshi?
Данные, которые я получаю, выглядят так:
{
# List<Warning>
"warnings": [{...}, {...}]
}
или вот так:
{
# Warning
"warnings": {...}
}
Я прочитал и нашел некоторые источники по этой теме, но нет примеров, и я не уверен, что делать дальше ... так что любые указатели будут очень признательны.
Попытка
Изначально я пытался установить автогенерированный адаптер моши, думая, что смогу построить на нем, но я не мог понять, что происходит, так что это меня ни к чему не привело. Но я все равно включил сгенерированный код на тот случай, если он все еще может быть полезен для задачи.
Изменить: это то, что у меня есть в настоящее время, хотя мне не очень нравится тот факт, что для работы требуется экземпляр Moshi.
Решение
Обобщенный подход с фабрикой
Я попытался перенести адаптер, который Эрик написал, на kotlin, поскольку с тех пор я понял, что более общий подход лучше, чем Эрик указывает в своем ответе. Как только это сработает, я перепишу части этого поста, чтобы сделать его более понятным, теперь это немного запутано, я прошу прощения за это.
РЕДАКТИРОВАТЬ: в итоге я использовал решение, предложенное Эриком в другом потоке, но перенесенное на 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()