目前,我使用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()
转换为 MyClass1
或 MyClass2
。
response.body() as MyClass1
也出现错误。
MyClass1
和 MyClass2
是普通的模板类。
class MyClass1( val id: String, val data: String)
有什么聪明的方法可以将响应正文转换为我的自定义类吗?
MyClass2 的小更新
class MyClass2( val token: String, val url: String, val quantity: Int)
最佳答案
正如 @Miha_x64 所提到的,Retrofit 不知道你的类( MyClass1
和 MyClass2
),因为你的 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>
如果您的回复没有 id
或data
元素,它们将只是 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/