【问题标题】:How can I get a specific JSON Array from the response using Retrofit2 and GSON?如何使用 Retrofit2 和 GSON 从响应中获取特定的 JSON 数组?
【发布时间】:2020-04-10 20:42:15
【问题描述】:
我正在尝试使用 Retrofit 从 JSON 格式中检索一些数据,并且只想要一个特定的数组作为响应。我怎样才能使用 GSON 做到这一点?例如,在下面的示例中,只需要 'articles' 数组。
status: "ok",
totalResults: 970843,
articles: [
{
...,
...,
...
},
【问题讨论】:
标签:
android
api
gson
retrofit2
【解决方案1】:
您可以为这种特殊情况创建一个自定义反序列化器,或者(我将描述第二个选项)有一个更通用的解决方案,需要付出更多的努力,但从长远来看会得到很好的回报运行,因为您可以将其应用于其他类似的用例。这是我最喜欢的方法,我在 GitHub 上发现了它(我不是发明者) - 并且自己也采用了 - 你可以检查代码和项目以了解它们如何协同工作 - project link
下面我摘录了相关片段:
- 这样创建一个特殊的转换器工厂:
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import okhttp3.ResponseBody
import retrofit2.Converter
import retrofit2.Retrofit
import java.lang.reflect.Type
/**
* A [Converter.Factory] which removes unwanted wrapping envelopes from API
* responses.
*/
class DenvelopingConverter(internal val gson: Gson) : Converter.Factory() {
override fun responseBodyConverter(
type: Type, annotations: Array<Annotation>?, retrofit: Retrofit?): Converter<ResponseBody, *>? {
// This converter requires an annotation providing the name of the payload in the envelope;
// if one is not supplied then return null to continue down the converter chain.
val payloadName = getPayloadName(annotations) ?: return null
val adapter = gson.getAdapter(TypeToken.get(type))
return Converter<ResponseBody, Any> { body ->
try {
val jsonReader = gson.newJsonReader(body.charStream())
jsonReader.beginObject()
while (jsonReader.hasNext()) {
if (payloadName == jsonReader.nextName()) {
return@Converter adapter.read(jsonReader)
} else {
jsonReader.skipValue()
}
}
return@Converter null
} finally {
body.close()
}
}
}
private fun getPayloadName(annotations: Array<Annotation>?): String? {
if (annotations == null) {
return null
}
for (annotation in annotations) {
if (annotation is EnvelopePayload) {
return annotation.value
}
}
return null
}
}
- 创建此注释类
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@Retention(AnnotationRetention.RUNTIME)
annotation class EnvelopePayload(val value: String = "")
- 在您的改造构建器中,添加您在第 1 步中创建的转换器
return Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(DenvelopingConverter(gson))
- 在您的改造界面中,使用您在第 2 步中创建的 @EnvelopePayload 过滤您想要的元素,在您的情况下为“文章”,就像这样
interface MyService {
@EnvelopePayload("articles")
@GET("whatever")
//fun search....
}
就是这样,享受吧!