[英]Retrofit2 & JSON response array with multiple possible values
我已经很长时间没有在这里写任何东西了,但现在我真的需要建议)
我使用 Retrofit2 作为 api 客户端。 服务器 API 有一个端点,例如/api/stats
接收 JSON 正文请求并返回 JSON响应为:
data class StatsResult<T>(
@SerializedName("code") val code: Int,
@SerializedName("message") val msg: String?,
@SerializedName("request_id") val requestId: String?,
@SerializedName("data") val data: T?
)
如果出现错误,则data
为空。 否则data
是一个数组,根据请求的类型可以包含不同类型的数据。
例如:
请求 1:
{
"type": "type1",
"params": {
}
}
Response:
{
"code": 0,
"request_id": "...",
"data": [
{
"key1": "value1",
"key2": "value2"
},
{
"key1": "value3",
"key2": "value4"
}
]
}
请求2:
{
"type": "type2",
"params": {
}
}
Response:
{
"code": 0,
"request_id": "...",
"data": [
{
"key3": "value1",
"key4": "value2"
},
{
"key3": "value3",
"key4": "value4"
}
]
}
简而言之,这是我的实现:
interface StatsApi {
@POST("/api/stats")
suspend fun getStats(@Body request: StatsRequest): ApiResponse<StatsData>
}
sealed class ApiResponse<out T: Any> {
data class Success<T: Any>(val body: T): ApiResponse<T>()
object Unauthorized : ApiResponse<Nothing>()
object Forbidden: ApiResponse<Nothing>()
object NetworkError: ApiResponse<Nothing>()
data class Error(val msg: String? = null): ApiResponse<Nothing>()
data class Exception(val t: Throwable): ApiResponse<Nothing>()
}
typealias StatsData = StatsResult<List<BaseStatsDto>>
open class BaseStatsDto()
class Type1StatsDto: BaseStatsDto() {
@SerializedName("key1") var key1: String? = null
@SerializedName("key2") var key2: String? = null
}
class Type2StatsDto: BaseStatsDto() {
@SerializedName("key3") var key3: String? = null
@SerializedName("key4") var key4: String? = null
}
因此,我尝试使用开放/抽象类BaseStatsDto
解决此问题,然后将其转换为最终类。 但是这个解决方案没有用。
对于响应处理,我使用CallAdapter.Factory()
和自定义Call<>
:
open class ApiResponseCall<T : Any>(
private val delegate: Call<T>
) : Call<ApiResponse<T>> {
override fun enqueue(callback: Callback<ApiResponse<T>>) {
return delegate.enqueue(object : Callback<T> {
override fun onFailure(call: Call<T>, t: Throwable) {
val rsp = when (t) {
is IOException -> ApiResponse.NetworkError
else -> ApiResponse.Exception(t)
}
callback.onResponse(this@ApiResponseCall, Response.success(rsp))
}
override fun onResponse(call: Call<T>, response: Response<T>) {
val rsp: ApiResponse<T>
rsp = if (response.isSuccessful) {
val body = response.body()
ApiResponse.Success(body as T)
} else {
val code = response.code()
val error = response.errorBody()
when (code) {
401 -> ApiResponse.Unauthorized
403 -> ApiResponse.Forbidden
in 400..499 -> ApiResponse.Error("Client error")
in 500..599 -> ApiResponse.Error("Server error")
else -> ApiResponse.Exception(Exception("Unknown error"))
}
}
callback.onResponse(this@ApiResponseCall, Response.success(rsp))
}
})
}
...
}
我看到了另一种解决方案 - 具有具有单独响应类型的单独接口功能。 它工作正常:
@POST("/api/stats")
suspend fun getType1Stats(@Body request: StatsRequest): ApiResponse<StatsResult<List<Type1StatsDto>>>
@POST("/api/stats")
suspend fun getType2Stats(@Body request: StatsRequest): ApiResponse<StatsResult<List<Type2StatsDto>>>
但是如果统计数据类型计数增加,维护起来会很不舒服。
我想要一个统计 api 端点。
使用http://www.jsonschema2pojo.org/设置 SourceType: JSON 和 Annonation style: Gson
请求 1 的数据
class Data1 {
@SerializedName("key1")
@Expose
var key1: String? = null
@SerializedName("key2")
@Expose
var key2: String? = null
}
请求的响应1
class Response1 {
@SerializedName("code")
@Expose
var code: Int? = null
@SerializedName("request_id")
@Expose
var requestId: String? = null
@SerializedName("data")
@Expose
var data: List<Data1>? = null
}
请求 2 的数据
class Data2 {
@SerializedName("key3")
@Expose
var key3: String? = null
@SerializedName("key4")
@Expose
var key4: String? = null
}
请求 2 的响应
class Response1 {
@SerializedName("code")
@Expose
var code: Int? = null
@SerializedName("request_id")
@Expose
var requestId: String? = null
@SerializedName("data")
@Expose
var data: List<Data2>? = null
}
和
@POST("/api/stats")
suspend fun getStats1(@Body request: StatsRequest1): ApiResponse<Response1>
@POST("/api/stats")
suspend fun getStats2(@Body request: StatsRequest2): ApiResponse<Response2>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.