关系为空时如何使用Retrofit 2消耗Spring Data Rest API响应?

时间:2018-07-05 09:53:58

标签: android retrofit2 spring-data-rest hal-json

我正在开发Android应用程序,并且正在使用Spring Rest Data编写后端。

场景:

假设我要列出用户最喜欢的产品。我需要调用GET /v1/users/4/favoriteProducts,以便在标头中提供访问令牌。如果用户拥有最喜欢的产品,我将收到如下响应:

{
    "links" : [ {
        "rel" : "self",
        "href" : "BASE_URL/v1/users/4/favoriteProducts",
        "hreflang" : null,
        "media" : null,
        "title" : null,
        "type" : null,
        "deprecation" : null
    } ],
    "content" : [ 
        {PRODUCT},
        {PRODUCT},
        {PRODUCT},
    ]
}

我可以对此进行反序列化而没有问题,我得到包含三个产品的HalList对象。我的翻新服务电话如下:

@GET("v1/users/{user}/favoriteProducts")
fun favoriteProducts(@Path("user") userId: Int): Call<HalList<Product>>

和我的HalList类:

data class HalList<T>(
    @SerializedName("links")
    val links: List<HalLink>,

    @SerializedName("content")
    var content: List<T>
)

但是如果我没有任何喜欢的产品,我会得到这样的东西:

{
    "links" : [ {
        "rel" : "self",
        "href" : "BASE_URL/v1/users/4/favoriteProducts",
        "hreflang" : null,
        "media" : null,
        "title" : null,
        "type" : null,
        "deprecation" : null
    } ],
   "content" : [ {
       "rel" : null,
       "collectionValue" : true,
       "relTargetType" : "com.example.entity.Product",
       "value" : [ ]
   } ]
}

当我用Retrofit解析它时,我得到HalList对象,其中content包含Product类的一个实例,根据类型将所有值设置为0或null或false,这会引起问题..

我为Gson编写了此TypeAdapter,以便在基于json的内容为空的情况下使用空列表覆盖内容

class HalTypeAdapterFactory : TypeAdapterFactory {
    override fun <T : Any?> create(gson: Gson?, type: TypeToken<T>?): TypeAdapter<T> {
        val delegate = gson?.getDelegateAdapter(this, type)!!

        if (!HalReflection.isResource(type?.rawType!!)) {
            return delegate
        }

        return HalTypeAdapter(gson, type, delegate)
    }
}

class HalTypeAdapter<T>(private val gson: Gson, private val type: TypeToken<T>, private val delegate: TypeAdapter<T>) : TypeAdapter<T>() {
    private val basicAdapter = gson.getAdapter(JsonElement::class.java)!!

    override fun write(out: JsonWriter?, value: T) {
        delegate.write(out, value)
    }

    override fun read(`in`: JsonReader?): T {
        val fullJson = basicAdapter.read(`in`)
        val deserialized = delegate.fromJsonTree(fullJson)

        if(type.rawType == HalList::class.java || type.rawType == HalPagedList::class.java) {
            if(fullJson.isJsonObject) {
                val content = fullJson.asJsonObject.getAsJsonArray("content")
                if(content[0].isJsonObject) {
                    val o = content[0].asJsonObject
                    if(o.has("collectionValue") && o.has("relTargetType")) {
                        val field = type.rawType.getDeclaredField("content")
                        field.isAccessible = true
                        field.set(deserialized, listOf<T>())
                        field.isAccessible = false
                    }
                }
            }
        }

        return deserialized
    }
}

但是我不确定这是否是正确的解决方案,还有其他更优雅的解决方案吗?也可以在后端执行任何操作以返回空列表,如下所示:

{
    "links" : [ {
        "rel" : "self",
        "href" : "BASE_URL/v1/users/4/favoriteProducts",
        "hreflang" : null,
        "media" : null,
        "title" : null,
        "type" : null,
        "deprecation" : null
    } ],
   "content" : []
}

编辑:最终解决方案

我不喜欢让Gson的委托适配器解析响应,然后检查json字符串并使用反射将其更改为空列表的想法。我改变了在反序列化之前修改json响应的方法:

class HalTypeAdapterFactory : TypeAdapterFactory {
    override fun <T : Any?> create(gson: Gson?, type: TypeToken<T>?): TypeAdapter<T> {
        val delegate = gson?.getDelegateAdapter(this, type)!!

        if (type?.rawType == HalList::class.java)
            return HalListTypeAdapter(gson, type, delegate)

        return delegate
    }
}

class HalListTypeAdapter<T>(
        private val gson: Gson,
        private val type: TypeToken<T>?,
        private val delegate: TypeAdapter<T>)
    : TypeAdapter<T>() {

    private val basicAdapter = gson.getAdapter(JsonElement::class.java)

    override fun write(out: JsonWriter?, value: T) {
        delegate.write(out, value)
    }

    override fun read(`in`: JsonReader?): T {
        val json = basicAdapter.read(`in`)

        if (json.isJsonObject) {
            val resource = json.asJsonObject
            val content = resource.getAsJsonArray("content")
            if (isEmptyCollection(content)) {
                resource.remove("content")
                resource.add("content", JsonArray())
            }
        }

        return delegate.fromJsonTree(json)
    }

    private fun isEmptyCollection(content: JsonArray): Boolean {
        if (content.size() == 1 && content[0].isJsonObject) {
            val first = content[0].asJsonObject
            return first.has("collectionValue") && first.has("relTargetType")
        }

        return false
    }
}

data class HalList<T>(
        @SerializedName("links")
        val links: List<HalReference>,

        @SerializedName("page")
        val page: HalListPageMeta?,

        @SerializedName("content")
        val content: List<T>
)

0 个答案:

没有答案