2

我正在尝试在我的 Android 项目中使用 moshi,但遇到了一些问题。

这是一个精简的示例 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_itemsScheduleItem这是一个包含3 个字段的数组:

  • type: 字符串标签,用于标识这是哪种类型的项目
  • item: 可以属于Event, Game, 和CheckIn
  • schedule_item_groups: 一个ScheduleGroup是更多的列表 - ScheduleItems

所以第一个问题是ScheduleGroup有一个列表,ScheduleItems每个项目都可以有自己的ScheduleGroup包含更多项目的列表。

第二个问题是item字段,它需要被实例化为三个类之一:EventGameCheckIn

我已经为此工作了一段时间,到目前为止,我一次只能让一个工作,但不能同时工作。

以下是数据类(我只包括了其中一个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()

这就是我让动态 Generic 类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_groupswhich is a 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 $.data[0].schedule_items[1]

4

2 回答 2

4

我不久前就想到了这一点,但我想我可以发布我所做的,以防将来对任何人有所帮助。

首先,我创建了一个临时中介类,用于在生成通用数据之前保存数据。

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构造函数。

于 2018-03-16T20:04:54.440 回答
-1

最近我遇到了类似的问题,我利用密封类和 JsonAdapter 来动态构建模型。我在另一篇文章中发布了我的答案,请参阅https://stackoverflow.com/a/56897476/5584709

于 2019-07-05T06:25:40.640 回答