Android:使用 Moshi 适配器解析内部非结构化 Json

Android: Parse inner unstructured Json with Moshi adapters

我无法解析 JSON(使用 Moshi)的某些内部部分,这些部分可能变化很大且高度非结构化。整体看起来像:

response: {
    items: [{
        type: "typeA",
        data: {
            "1563050214700-001": {
                foo: 123 ....
            }
        }
    }, {
        type: "typeB",
        data: {
            "1563050214700-002": {[
                // differs a lot from previous one
                {bar: 123 .... }
            ]}
        }
    }]
}

数据class结构如下:

data class Response(
    val items: Map<String,List<Item?>>?
) {
    data class Item(
        val type: String?,
        val data: Map<String,List<DataItem?>>?
    ) {
        data class DataItem(
            // members highly unstructured
        )
    }
}

"DataItem" 的架构变化很大。看起来 Moshi codegen 支持可能允许手动解析这些内部数据 classes 的适配器,但我找不到合适的教程或示例。理想情况下,我希望整个 Response 就像定义明确的 JSON.

一样被解析

我是这样使用的retrofit/moshi

@Provides
@Singleton
@MyApp
fun provideMyAppRetrofit(context: Context, @MyApp okHttpClient: OkHttpClient): Retrofit {
    return Retrofit.Builder()
        .client(okHttpClient)
        .addConverterFactory(MoshiConverterFactory.create())
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
        .baseUrl(context.getString(R.string.APP_BASE_URL))
        .build()
}

@Provides
@Singleton
fun provideMyAppApiService(@MyApp retrofit: Retrofit): MyAppApiService {
    return retrofit.create(MyAppApiService::class.java)
}

如何实现?任何示例或参考实施都会有所帮助。

欢迎来到多态JSON解析问题词!

我们正在编写自己的 JSON 适配器,看起来像:

internal class CardJsonAdapter(
        moshi: Moshi
) : JsonAdapter<Card>() {

    private val cardTypeAdapter = moshi.adapter(Card.Type::class.java)
    private val amountsWithActionAdapter = moshi.adapter(AmountsWithActionCard::class.java)
    private val backgroundImageCardAdapter = moshi.adapter(BackgroundImageCard::class.java)

    @Suppress("TooGenericExceptionCaught")
    override fun fromJson(reader: JsonReader): Card = try {

        @Suppress("UNCHECKED_CAST")
        val jsonMap = reader.readJsonValue() as Map<String, Any?>
        val type = cardTypeAdapter.fromJsonValue(jsonMap["type"])
        createCardWithType(type, jsonMap)

    } catch (error: Exception) {
        if (BuildConfig.DEBUG) {
            Log.w("CardJsonAdapter", "Failed to parse card", error)
        }
        // Try not to break the app if we get unexpected data: ignore errors and return a placeholder card instead.
        UnknownCard
    }

    override fun toJson(writer: JsonWriter, value: Card?) {
        throw NotImplementedError("This adapter cannot write cards to JSON")
    }

    private fun createCardWithType(type: Type?, jsonMap: Map<String, Any?>) = when (type) {
        null -> UnknownCard
        Type.AMOUNTS_WITH_ACTION -> amountsWithActionAdapter.fromJsonValue(jsonMap)!!
        Type.BACKGROUND_IMAGE_WITH_TITLE_AND_MESSAGE -> backgroundImageCardAdapter.fromJsonValue(jsonMap)!!
    }
}

但是,它不再是必需的。 Moshi 现在支持多态 JSON 解析 - https://proandroiddev.com/moshi-polymorphic-adapter-is-d25deebbd7c5

制作自定义适配器

class YourAdapter {

@FromJson
fun fromJson(reader: JsonReader, itemsAdapter: JsonAdapter<ItemsResponse>): List<ItemsResponse>? {
    val list = ArrayList<ItemsResponse>()
    if (reader.hasNext()) {
        val token = reader.peek()
        if (token == JsonReader.Token.BEGIN_ARRAY) {
            reader.beginArray()
            while (reader.hasNext()) {
                val itemResponse = itemsAdapter.fromJsonValue(reader.readJsonValue())
                itemsResponse?.let {
                    list.add(itemResponse)
                }
            }
            reader.endArray()
        }
    }
    return list
}
}