繁体   English   中英

具有多个可能值的 Retrofit2 和 JSON 响应数组

[英]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.

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