Moshi: разобрать отдельный объект или список объектов (kotlin) - PullRequest
0 голосов
/ 16 ноября 2018

Я создаю приложение, которое может извлекать объект Warning из API, которым я не управляю, и они не хотят удалять это поведение, с которым я пытаюсь обойтись.

Я хотел бычтобы иметь возможность анализировать один Warning объект или List<Warning> объекты.(Поскольку API возвращает либо 1 объект, если существует только 1, либо> 1 список объектов.)

Вопрос

Как можно проанализировать объект как список напрямую или проанализировать список с помощьюМоши?

Данные, которые я получаю, выглядят так:

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

или примерно так:

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

Я прочитал и нашел несколько источников наэта тема, но нет примеров, и я не уверен, как поступить .. поэтому любые указатели будут высоко оценены.

Попытка

Я изначально пыталсярожок для обуви, сгенерированный автоматически, адаптер для моши, который думал, что я мог бы создать его поверх него, но я не мог понять, что происходит, поэтому это ни к чему меня не привело.Но я все равно включил сгенерированный код, если он еще может быть полезен для этой задачи.

Редактировать : Это то, что у меня сейчас есть, хотя я не очень доволен тем, что этоберет экземпляр Моши на работу.

Решение

Обобщенный подход с фабрикой

Я пытался портировать адаптер, который Эрик написал в 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()

Связанные проблемы / темы:

1 Ответ

0 голосов
/ 19 ноября 2018

API возвращает либо 1 объект, если список объектов содержит только 1, либо> 1.

Создайте адаптер, который ищет, если вы получаете массив в первую очередь. Здесь именно то, что вы хотите. Он включает квалификатор, поэтому вы можете применять его только к спискам, которые могут иметь такое поведение для отдельных элементов. @SingleToArray List<Warning>.

Существует еще один пример работы с несколькими форматами здесь для дальнейшего чтения.

...