java - 将 2 响应主体改造为自定义类

标签 java kotlin retrofit2

目前,我使用retrofit2调用 Restful api 并获得响应。由于响应体可以有多种类型,所以我编写了以下代码。

//Interface
@FormUrlEncoded
@POST("payments/events/{id}")
fun postPayment(@Path("id") id: String): Call<Any>

//Api Manager
fun postPayment(id: String): Observable<Any> {
    return Observable.create {
        subscriber ->
        val callResponse = api.postPayment(id)
        val response = callResponse.execute()

        if (response.isSuccessful) {
            if (response.body() is MyClass1) {
                // never success... 
            } else if (response.body() is MyClass2) {
                // never success...
            }
            subscriber.onNext(response.body())
            subscriber.onCompleted()
        } else {
            subscriber.onError(Throwable(response.message()))
        }
    }
}

所以我无法将 response.body() 转换为 MyClass1MyClass2response.body() as MyClass1 也出现错误。

MyClass1MyClass2 是普通的模板类。

class MyClass1( val id: String, val data: String)

有什么聪明的方法可以将响应正文转换为我的自定义类吗?

MyClass2 的小更新

class MyClass2( val token: String, val url: String, val quantity: Int)

最佳答案

正如 @Miha_x64 所提到的,Retrofit 不知道你的类( MyClass1MyClass2 ),因为你的 Call使用 Any类型。因此,Retrofit 不会创建 MyClass1 的实例或MyClass2 ,相反,它只是创建 Any 的实例类。

最简单的解决方案就是组合这两个类:

data class MyClass(
    val id: String?,
    val data: String?,
    val token: String?,
    val url: String?,
    val quantity: Int
)

然后您可以在界面中指定响应类型:

@FormUrlEncoded
@POST("payments/events/{id}")
fun postPayment(@Path("id") id: String): Call<MyClass>

如果您的回复没有 iddata元素,它们将只是 null 。然后,您只需检查哪些值是 null 即可检查收到的响应类型。 :

if (response.body().id != null) {
    // Handle type 1 response...
} else if (response.body().token != null) {
    // Handle type 2 response...
}

一个稍微复杂的解决方案是为两个类编写一个包装器,并使用一个类型适配器来填充该包装器。这将避免每个字段的可为空性,并保持数据结构分离。

这会根据ConverterFactory而有所不同你正在使用,但如果,例如,你正在使用 Gson,它会看起来像这样:

data class ApiResponse(
    val val1: MyClass1? = null,
    val val2: MyClass2? = null
)

class ApiResponseAdapter : TypeAdapter<ApiResponse> {

    @Throws(IOException::class)
    override fun write(out: JsonWriter, value: ApiResponse?) {
        if (value != null) {
            out.beginObject()

            value.val1?.id? let { out.name("id").value(it) }
            value.val1?.data? let { out.name("data").value(it) }
            value.val2?.token? let { out.name("token").value(it) }
            value.val2?.url? let { out.name("url").value(it) }
            value.val2?.quantity? let { out.name("quantity").value(it) }

            out.endObject()
        } else {
            out.nullValue()
        }
    }

    @Throws(IOException::class)
    override fun read(in: JsonReader): ApiResponse {
        reader.beginObject()

        var id: String? = null
        var data: String? = null
        var token: String? = null
        var url: String? = null
        var quantity: Int = 0

        while(in.hasNext()) {
            val name = in.nextName()

            if (name.equals("id", true)) {
                id = in.nextString()
            } else if (name.equals("data", true)) {
                data = in.nextString()
            } else if (name.equals("token", true)) {
                token = in.nextString()
            } else if (name.equals("url", true)) {
                url = in.nextString()
            } else if (name.equals("quantity", true)) {
                quantity = in.nextInt()
            }
        }

        reader.endObject()

        if (id != null && data != null) {
            return ApiResponse(MyClass1(id, data), null)
        } else if (token != null && url != null) {
            return ApiResponse(null, MyClass2(token, url, quantity))
        } else {
            return ApiResponse()
        }
    }

}

然后您可以将此类型适配器添加到您的 Gson 实例中:

val gson = GsonBuilder().registerTypeAdapter(ApiResponse::class.java, ApiResponseAdapter()).create()

然后替换Call<Any>输入 Call<ApiRepsone>然后您可以通过检查哪个值是 null 来检查收到的响应:

if (response.body().val1 != null) {
    // Handle MyClass1 response...
} else if (response.body().val2 != null) {
    // Handle MyClass2 response...
}

关于java - 将 2 响应主体改造为自定义类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44699841/

相关文章:

kotlin - Retrofit2 + SimpleXML日期转换器

java - 为什么我们提出接口(interface)改造请求

java - 基于 MATLAB 的 Java 程序

具有两种类型的 Java 自引用泛型

Java Realm 获取最新插入的项目

arrays - 具有通用2D数组的通用函数

Android Wear 2 WiFi本地网络接入

java - Errors/BindingResult 参数应在模型属性、@RequestBody 或 @RequestPart 参数之后立即声明

android - 类 'kotlin.Unit' 是使用不兼容的 Kotlin 版本编译的。其元数据的二进制版本是1.6.0,预期版本是1.1.15

unit-testing - 协程测试失败, "This job has not completed yet"