如何使用 Retrofit2 和 GSON 从响应中获取特定的 JSON 数组?

How can I get a specific JSON Array from the response using Retrofit2 and GSON?

我正在尝试使用 Retrofit 从 JSON 格式中检索一些数据,并且只需要特定的数组形式的响应。我如何使用 GSON 做到这一点?例如,在下面的示例中,只需要 'articles' 数组。


status: "ok",
totalResults: 970843,
articles: [
{
   ...,
   ...,
   ...
},

您可以为这种特殊情况创建一个自定义反序列化器,或者(第二个选项是我将描述的)有一个更通用的解决方案,需要付出更多的努力,但会得到很好的回报长 运行,因为您可以将它应用于其他类似的用例。这是我最喜欢的方法,我在 GitHub 上遇到过它(我不是发明者) - 并且自己也采用了 - 你可以检查代码和项目以了解它们如何协同工作 - project link

下面是我摘录的相关片段:

  1. Create a special converter factory as such:
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
    }
}
  1. Create this annotation class
@Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
@Retention(AnnotationRetention.RUNTIME)
annotation class EnvelopePayload(val value: String = "")
  1. In your retrofit builder, add the converter you created in step 1
  return Retrofit.Builder()
                .baseUrl(baseUrl)
                .addConverterFactory(DenvelopingConverter(gson))
  1. In your retrofit interface, use the @EnvelopePayload you created in step 2 to filter the elements you want, in your case, "articles", like so
interface MyService {

    @EnvelopePayload("articles")
    @GET("whatever")
    //fun search....
}

就是这样,尽情享受吧!