远程服务器返回带有正文的响应:
{
done: true,
response: {
result: {
// data
}
}
}
如果出现错误,带有
200
状态和正文的响应:
{
done: false,
error: {
message: ""
}
}
在将 JSON 传输到类型化实体之前,我想将
result
数据设置为响应的根。
{
// data
}
我无法为Retrofit接口的所有功能创建一个包装模型(这是一个很长的故事)。
我创建了一个自定义工厂
ProxyGsonConverterFactory
(基于GsonConverterFactory.java)。它按预期工作。
看
GsonResponseBodyConverter
课程。为了更改 json 根,该类使用 org.json.JSONObject
。它将字符串解析为对象并获得 resultData
。然后将 resultData
转换回字符串并使用 gson
将其转换为通用 T
。
class ProxyGsonConverterFactory (
private val gson: Gson
) : Converter.Factory() {
override fun responseBodyConverter(
type: Type, annotations: Array<Annotation>, retrofit: Retrofit
): Converter<ResponseBody, *> {
val adapter = gson.getAdapter(TypeToken.get(type))
return GsonResponseBodyConverter(gson, adapter)
}
override fun requestBodyConverter(
type: Type,
parameterAnnotations: Array<Annotation>,
methodAnnotations: Array<Annotation>,
retrofit: Retrofit
): Converter<*, RequestBody> {
val adapter = gson.getAdapter(TypeToken.get(type))
return GsonRequestBodyConverter(gson, adapter)
}
}
internal class GsonResponseBodyConverter<T>(
private val gson: Gson,
private val adapter: TypeAdapter<T>
) : Converter<ResponseBody, T> {
@Throws(IOException::class)
override fun convert(value: ResponseBody): T {
val jsonObject = JSONObject(value.string())
val jsonPayload = jsonObject.optJSONObject("response")?.opt("result")
if (jsonPayload == null) {
val errorMessage = jsonObject.optJSONObject("error")?.toString()
throw ProxyExecutionException(errorMessage ?: "GsonResponseBodyConverter: payload is null")
}
val modifiedJsonStr = jsonPayload.toString()
val jsonReader = gson.newJsonReader(modifiedJsonStr.reader())
val result = adapter.read(jsonReader)
if (jsonReader.peek() != JsonToken.END_DOCUMENT) {
throw JsonIOException("JSON document was not fully consumed.")
}
jsonReader.close()
return result
}
}
internal class GsonRequestBodyConverter<T>(
private val gson: Gson,
private val adapter: TypeAdapter<T>
) : Converter<T, RequestBody> {
@Throws(IOException::class)
override fun convert(value: T): RequestBody {
val buffer = Buffer()
val writer = OutputStreamWriter(buffer.outputStream(), StandardCharsets.UTF_8)
val jsonWriter = gson.newJsonWriter(writer)
adapter.write(jsonWriter, value)
jsonWriter.close()
return buffer.readByteString().toRequestBody(MEDIA_TYPE)
}
companion object {
private val MEDIA_TYPE: MediaType = "application/json; charset=UTF-8".toMediaType()
}
}
GsonResponseBodyConverter
解析 JSON 字符串两次。使用 org.json.JSONObject
需要更多时间。我可以只用一次解析来解决任务吗?
例如,函数
ProxyGsonConverterFactory.responseBodyConverter
包含泛型T的Type
来创建gson
适配器。我们可以将 T 的适配器放入父适配器ResponseWrapper
,以便解析一次然后仅返回 T 主体吗?
data class ResponseWrapper <T>(
val done: boolean
val response: ResponseBody<T>
)
data class ResponseBody <T>(
val result: T
)
除了将
result
数据移动到根目录之外,您还可以尝试 Kotlin 扩展。
data class ResponseWrapper<T>(
val done: Boolean,
val response: ResponseBody<T>?,
val error: JsonElement?
)
data class ResponseBody<T>(
val result: T
)
// ResponseWrapper extension for result
val <T> ResponseWrapper<T>.result: T? get() = this.response?.result
如果没有找到 JSON 键,Gson 会自动将
设置为可为 null 的变量。null
您可以从这样的回复中轻松获得
result
。
val apiResponse = api.test()
val isDone = apiResponse.done
val result = apiResponse.result