改装2响应正文类的机体

改装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()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) 
+0

为什么不使用自定义对象来接收您的响应?就像'Call ' – Marce

+0

正如我所描述的,在我的情况下,响应类型可以是多种类型 - MyClass1或MyClass2。 – Igor

+0

有没有('MyClass1'和'MyClass2')太多不同了? – Marce

首先,感谢@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,改造不知道你的类(MyClass1MyClass2)提到的小更新,因为你Call使用Any型。因此,Retrofit不会创建一个MyClass1MyClass2的实例,而只是创建一个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<ApiRepsone>取代Call<Any>类型,然后可以检查其响应通过检查其值是null接收:

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