改装2响应正文类的机体
问题描述:
目前,我使用retrofit2来调用restful apis并获得响应。因为响应主体可以是多种类型,所以我写了下面的代码。改装2响应正文类的机体
//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)
答
首先,感谢@Bryan的回答。你的回答很完美,但最后我做了一些棘手的方法。
...
if (response.isSuccessful) {
val jsonObject = JSONObject(response.body() as Map<*, *>)
val jsonString = jsonObject.toString()
if (jsonObject.has("id")) {
val myclass1Object = Gson().fromJson(jsonString, MyClass1::class.java)
...
} else {
val myclass2Object = Gson().fromJson(jsonString, MyClass2::class.java)
...
}
}
...
答
正如@ Miha_x64,改造不知道你的类(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<ApiRepsone>
取代Call<Any>
类型,然后可以检查其响应通过检查其值是null
接收:
if (response.body().val1 != null) {
// Handle MyClass1 response...
} else if (response.body().val2 != null) {
// Handle MyClass2 response...
}
为什么不使用自定义对象来接收您的响应?就像'Call' –
Marce
正如我所描述的,在我的情况下,响应类型可以是多种类型 - MyClass1或MyClass2。 – Igor
有没有('MyClass1'和'MyClass2')太多不同了? – Marce