Адаптер Android и Moshi с универсальным типом

Я пытаюсь использовать moshi в своем проекте Android, но у меня возникают некоторые проблемы.

Вот урезанный образец JSON

{
  "data": [
    {
      "label": "May",
      "schedule_items": [
        {
          "type": "event",
          "item": {
            "foo": "bar",
            "some_prop": 1
          },
          "schedule_item_groups": [
            {
              "label": "Friday May 4th",
              "schedule_items": [
                {
                  "type": "check_in",
                  "item": {
                    "a_different_prop": 15
                  },
                  "schedule_item_groups": null
                },
                {
                  "type": "game",
                  "item": {
                    "yet_another_different_prop": 3598
                  },
                  "schedule_item_groups": null
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

Как видите, это список из ScheduleGroups, и внутри этого объекта у вас есть метка и schedule_items. Это массив ScheduleItem с 3 полями:

  • type: Строковая метка, определяющая, какой это тип Предмета.
  • item: может относиться к классу Event, Game и CheckIn
  • schedule_item_groups: ScheduleGroup, который представляет собой список других - ScheduleItems

Итак, первая проблема заключается в том, что ScheduleGroup имеет список ScheduleItems, и каждый элемент может иметь свой собственный список ScheduleGroup, содержащий больше элементов.

Вторая проблема - это поле item, его нужно создать как один из трех классов: Event, Game, CheckIn.

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

Вот классы данных (я включил только один из Item классов):

data class ScheduleGroup(
    val label: String,
    val schedule_items: List<ScheduleItem<Any>>
)
data class ScheduleItem<out T>(
    val type: String,
    val schedule_item_groups: List<ScheduleGroup>
    val item: T
) {
    abstract class Item
}
data class Event(
    val some_prop: Int,
    val foo: String
) : ScheduleItem.Item()

Вот как я заставил работать динамический универсальный класс Item:

@FromJson
fun fromJson(map: Map<*, *>): ScheduleItem<Any> {
    val moshi: Moshi = Moshi.Builder().build()
    val type: String = map["type"] as String
    val itemJson = moshi.adapter(Map::class.java).toJson(map["item"] as Map<*, *>)

    val item = when (type) {
        EventType.EVENT -> moshi.adapter(Event::class.java).fromJson(itemJson)
        EventType.GAME -> moshi.adapter(Game::class.java).fromJson(itemJson)
        EventType.CHECK_IN, EventType.CHECK_OUT ->
            moshi.adapter(CheckIn::class.java).fromJson(itemJson)
        else -> throw Error("Unknown type was found $type")
    }

val scheduleGroupType = Types.newParameterizedType(List::class.java, ScheduleGroup::class.java)
    @Suppress("UNCHECKED_CAST")
    val scheduleGroupJson = moshi.adapter<List<ScheduleGroup>>(scheduleGroupType)
        .toJson(map["schedule_item_groups"] as List<ScheduleGroup>?)

    val list: List<ScheduleGroup>? = moshi
        .adapter<List<ScheduleGroup>>(scheduleGroupType).fromJson(scheduleGroupJson)

    return ScheduleItem(type, list ?: listOf(), item)
}

Он правильно создаст правильный Item класс, но когда я пытаюсь добавить List<ScheduleGroup>, я получаю ошибки, и что бы я ни делал, я не могу заставить оба работать.

Редактировать:

Я обновил код, чтобы показать, что я использую, чтобы попытаться десериализовать schedule_item_groups, который является List<ScheduleGroup>.

Я получаю сообщение об ошибке: (Это другая ошибка, чем я получал раньше ...)

com.squareup.moshi.JsonDataException: java.lang.IllegalArgumentException: невозможно установить окончательное поле java.lang.String com.roomroster.mobile_android.data.api.schedule.models.ScheduleGroup.label в com.squareup.moshi.LinkedHashTreeMap at $ .data [0] .schedule_items [1]


person jordond    schedule 21.02.2018    source источник
comment
Не могли бы вы опубликовать сообщения об ошибках?   -  person dazza5000    schedule 21.02.2018
comment
Я добавил код, который использую, и ошибку.   -  person jordond    schedule 21.02.2018
comment
Возможно, вы сможете адаптировать что-то из github.com/square/moshi/pull/264/ файлы   -  person Eric Cochran    schedule 23.02.2018
comment
В будущем мы получим лучшую документацию по этому варианту использования.   -  person Eric Cochran    schedule 23.02.2018


Ответы (2)


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

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

data class ScheduleItem<T>(
    val date: Date,
    val type: String,
    var scheduleGroups: List<ScheduleGroup> = listOf(),
    var item: T
) {
    data class ScheduleItemJson(
        val date: Date,
        val type: String,
        val schedule_item_groups: List<ScheduleGroup>? = listOf(),
        val item: Any
    )
}

Потом в переходнике

@FromJson fun fromJson(item: ScheduleItem.ScheduleItemJson): ScheduleItem<Any> {
    val moshi: Moshi = Moshi.Builder().build()
    val json = moshi.adapter(Map::class.java).toJson(item.item as Map<*, *>)

    return ScheduleItem(
        item.date,
        item.type,
        item.schedule_item_groups ?: listOf(),
        when (item.type) {
            ItemType.GAME -> moshi.adapter(GameItem::class.java).fromJson(json)
            ItemType.EVENT -> moshi.adapter(EventItem::class.java).fromJson(json)
            ItemType.CHECK_IN, ItemType.CHECK_OUT ->
                moshi.adapter(ReservationItem::class.java)
                     .fromJson(json).apply { this!!.type = item.type }
            else -> ScheduleItem.NullItem()
        }!!
    )
}

Оператор when создает <T : Item> и передает его конструктору ScheduleItem.

person jordond    schedule 16.03.2018

Недавно я столкнулся с подобной проблемой, я использовал запечатанный класс и JsonAdapter для динамического построения моделей. Я опубликовал свой ответ в другом сообщении, см. https://stackoverflow.com/a/56897476/5584709

person Jegan Babu    schedule 05.07.2019