简体   繁体   English

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

[英]Retrofit 2 response body to custom class

Currently, I use retrofit2 to call restful apis and get response. 目前,我使用retrofit2来调用restful apis并获得响应。 Because the response body can be multiple types, I wrote the code following. 因为响应体可以是多种类型,所以我编写了以下代码。

//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()))
        }
    }
}

So I'm not able to cast response.body() to MyClass1 or MyClass2 . 所以我无法将response.body()MyClass1MyClass2 response.body() as MyClass1 occurs error too. response.body() as MyClass1发生错误。

MyClass1 and MyClass2 are normal template classes. MyClass1MyClass2是普通的模板类。

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

Is there any smart way to cast response body to my custom classes? 有没有聪明的方法将响应体转换为我的自定义类?

Small update for MyClass2 MyClass2的小更新

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

As mentioned by @Miha_x64, Retrofit doesn't know about your classes ( MyClass1 and MyClass2 ) because your Call uses the Any type. 正如@ Miha_x64所述,Retrofit不了解您的类( MyClass1MyClass2 ),因为您的Call使用Any类型。 Therefore, Retrofit is not creating an instance of MyClass1 or MyClass2 , instead it is just creating an instance of the Any class. 因此,Retrofit不会创建MyClass1MyClass2的实例,而只是创建Any类的实例。

The simplest solution would just be to combine the two classes: 最简单的解决方案就是将两个类组合在一起:

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

Then you can specify the response type in your interface: 然后,您可以在界面中指定响应类型:

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

In the case your response does not have an id or data element, they will just be null . 如果您的响应没有iddata元素,则它们将为null Then you can check which type of response was received simply by checking which values are null : 然后,您可以通过检查哪些值为null来检查收到的响应类型:

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

A slightly more complex solution would be to write a wrapper for your two classes, and a type adapter to populate the wrapper. 稍微复杂的解决方案是为两个类编写一个包装器,并使用一个类型适配器来填充包装器。 This would avoid the nullability of each of the fields, as well as keep your data structure separated. 这样可以避免每个字段的可空性,并保持数据结构的分离。

This would differ based on the ConverterFactory you are using but if, for example, you are using Gson, it would look something like this: 这将根据您使用的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()
        }
    }

}

Then you can add this type adapter to your Gson instance: 然后,您可以将此类型适配器添加到您的Gson实例:

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

Then replace the Call<Any> type with Call<ApiRepsone> and you can then check which response was received by checking which value is null : 然后用Call<ApiRepsone>替换Call<Any>类型,然后通过检查哪个值为null来检查收到的响应:

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

First of all, thanks @Bryan for answer. 首先,感谢@Bryan的回答。 Your answer was perfect but finally I did something tricky way. 你的答案是完美的,但最后我做了一些棘手的事情。

...
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)
        ...
    }
}
...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM