在Moshi自定义类型适配器之间传递信息

时间:2018-03-26 11:43:27

标签: android moshi

我正在使用Moshi从我们的服务器反序列化json,但我遇到了一个问题,我确信有一个解决方案,我只是看不到它。在套接字上,我们发送json,在顶层有三个字段:

{
    "data_type": "<actual_data_type>",
    "data_id": "<actual_data_id>",
    "data": <data_object>
}

问题是data实际上可能是几个不同的对象,基于data_type是什么我不能确定如何将该信息传递到适配器Data。我尝试了几个不同的东西,但它越来越接近我自己解析整个事情,这似乎打败了这一点。有没有办法将信息从一个适配器传递到另一个适配器?

1 个答案:

答案 0 :(得分:0)

对于任何想要做类似事情的人,我从这里采用了通用工厂的基本形状:https://github.com/square/moshi/pull/264/files(这也是@eric cochran在他的评论中推荐的内容)并使其更具体适合我的确切案例。

class EventResponseAdapterFactory : JsonAdapter.Factory {
    private val labelKey = "data_type"
    private val subtypeToLabel = hashMapOf<String, Class<out BaseData>>(
        DataType.CURRENT_POWER.toString() to CurrentPower::class.java,
        DataType.DEVICE_STATUS_CHANGED.toString() to DeviceStatus::class.java,
        DataType.EPISODE_EVENT.toString() to EpisodeEvent::class.java,
        DataType.APPLIANCE_INSTANCE_UPDATED.toString() to ApplianceInstanceUpdated::class.java,
        DataType.RECURRING_PATTERNS.toString() to RecurringPatternOccurrence::class.java,
        DataType.RECURRING_PATTERN_UPDATED.toString() to RecurringPatternUpdated::class.java
    )

    override fun create(type: Type, annotations: Set<Annotation>, moshi: Moshi): JsonAdapter<*>? {
        if (!annotations.isEmpty() || type != EventResponse::class.java) {
            return null
        }

        val size = subtypeToLabel.size
        val labelToDelegate = LinkedHashMap<String, JsonAdapter<EventResponse<BaseData>>>(size)
        for (entry in subtypeToLabel.entries) {
            val key = entry.key
            val value = entry.value
            val parameterizedType = Types.newParameterizedType(EventResponse::class.java, value)
            val delegate = moshi.adapter<EventResponse<BaseData>>(parameterizedType, annotations)
            labelToDelegate.put(key, delegate)
        }

        return EventResponseAdapter(
            labelKey,
            labelToDelegate
        )
    }

    private class EventResponseAdapter internal constructor(
        private val labelKey: String,
        private val labelToDelegate: LinkedHashMap<String, JsonAdapter<EventResponse<BaseData>>>
    ) : JsonAdapter<EventResponse<BaseData>>() {

        override fun fromJson(reader: JsonReader): EventResponse<BaseData>? {
            val raw = reader.readJsonValue()
            if (raw !is Map<*, *>) {
                throw JsonDataException("Value must be a JSON object but had a value of $raw of type ${raw?.javaClass}")
            }

            val label = raw.get(labelKey) ?: throw JsonDataException("Missing label for $labelKey")
            if (label !is String) {
                throw JsonDataException("Label for $labelKey must be a string but had a value of $label of type ${label.javaClass}")
            }
            val delegate = labelToDelegate[label] ?: return null
            return delegate.fromJsonValue(raw)
        }

        // Not used
        override fun toJson(writer: JsonWriter, value: EventResponse<BaseData>?) {}
    }
}

唯一需要注意的是,链接中的RuntimeJsonAdapterFactory使用Types.getRawType(type)来获取剥离了泛型的类型。当然,我们不希望这样,因为一旦找到特定的泛型类型,我们就希望正常的Moshi适配器能够启动并为我们进行正确的解析。