Java 将2响应体改装为定制级

Java 将2响应体改装为定制级,java,kotlin,retrofit2,Java,Kotlin,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 Observa

目前,我使用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()))
        }
    }
}
有没有什么聪明的方法可以将响应主体转换为我的自定义类

MyClass2的小更新

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

正如@Miha_x64所提到的,改装不知道您的类(
MyClass1
MyClass2
),因为您的
调用使用
Any
类型。因此,改型并不是创建
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>

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

根据您使用的
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()
        }
    }

}
然后将
Call
类型替换为
Call
,然后您可以通过检查哪个值为
null
来检查收到了哪个响应:

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

正如@Miha_x64所提到的,改装不知道您的类(
MyClass1
MyClass2
),因为您的
调用使用
Any
类型。因此,改型并不是创建
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>

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

根据您使用的
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()
        }
    }

}
然后将
Call
类型替换为
Call
,然后您可以通过检查哪个值为
null
来检查收到了哪个响应:

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

首先,感谢@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)
        ...
    }
}
...
。。。
if(response.issucessful){
val jsonObject=jsonObject(response.body()作为映射)
val jsonString=jsonObject.toString()
if(jsonObject.has(“id”)){
val myclassobject=Gson().fromJson(jsonString,MyClass1::class.java)
...
}否则{
val myclass2Object=Gson().fromJson(jsonString,MyClass2::class.java)
...
}
}
...

首先,感谢@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)
        ...
    }
}
...
。。。
if(response.issucessful){
val jsonObject=jsonObject(response.body()作为映射)
val jsonString=jsonObject.toString()
if(jsonObject.has(“id”)){
val myclassobject=Gson().fromJson(jsonString,MyClass1::class.java)
...
}否则{
val myclass2Object=Gson().fromJson(jsonString,MyClass2::class.java)
...
}
}
...

为什么不使用自定义对象来接收响应?类似于我描述的
Call
,在我的例子中,响应类型可以是多种类型-MyClass1或MyClass2。是否有(
MyClass1
MyClass2
)太多不同?不幸的是,是的。改装对
MyClass1
MyClass2
一无所知,您可能需要编写自定义ResponseByConverter。为什么不使用自定义对象来接收响应?类似于我描述的
Call
,在我的例子中,响应类型可以是多种类型-MyClass1或MyClass2。是否有(
MyClass1
MyClass2
)太多不同?不幸的是,是的。改装对
MyClass1
MyClass2
一无所知,您可能需要编写一个自定义responseBodyConverter。