【问题标题】: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

    下面我摘录了相关片段:

    1. 这样创建一个特殊的转换器工厂:
    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. 创建此注释类
    @Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER, AnnotationTarget.PROPERTY_SETTER)
    @Retention(AnnotationRetention.RUNTIME)
    annotation class EnvelopePayload(val value: String = "")
    
    1. 在您的改造构建器中,添加您在第 1 步中创建的转换器
      return Retrofit.Builder()
                    .baseUrl(baseUrl)
                    .addConverterFactory(DenvelopingConverter(gson))
    
    1. 在您的改造界面中,使用您在第 2 步中创建的 @EnvelopePayload 过滤您想要的元素,在您的情况下为“文章”,就像这样
    interface MyService {
    
        @EnvelopePayload("articles")
        @GET("whatever")
        //fun search....
    }
    

    就是这样,享受吧!

    【讨论】:

      猜你喜欢
      • 2013-05-12
      • 2020-04-01
      • 2020-05-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-12-23
      • 1970-01-01
      • 2017-10-13
      相关资源
      最近更新 更多