【问题标题】:How to handle JSONP response through Retrofit and GsonConverter?如何通过 Retrofit 和 GsonConverter 处理 JSONP 响应?
【发布时间】:2017-05-01 23:40:00
【问题描述】:

我需要解析来自 Flickr API 的响应。
http://api.flickr.com/services/feeds/photos_public.gne?tagmode=any&format=json

在 jsonFlickrFeed jQuery 回调函数中返回响应(不是有效的 JSON 响应)。

我知道我们可以使用 nojsoncallback=1 查询删除 Flickr API 的 JSON 回调方法。

但是,如果必须使用带填充的 JSON (JSONP),是否有更好的方法来处理 JSONP 响应?

而不是将响应作为字符串获取,然后修剪 JSON 填充,然后解析剩余的 JSON 数据。

Flickr API 响应示例-

jsonFlickrFeed({
"title": "Recent Uploads tagged mountrainier",
"link": "http:\/\/www.flickr.com\/photos\/tags\/mountrainier\/",
"description": "",
"modified": "2016-12-15T16:56:42Z",
"generator": "http:\/\/www.flickr.com",
"items": [ {
    "title": "Gateway Arts District Open Studio Tour, December 10, 2016",
    "link": "http:\/\/www.flickr.com\/photos\/kimsworldofart\/31274762970\/",
    "media": {
        "m": "http:\/\/farm1.staticflickr.com\/381\/31274762970_c40599d623_m.jpg"
    },
    "date_taken": "2016-12-10T15:49:03-08:00",
    "description": " <p><a href=\"http:\/\/www.flickr.com\/people\/kimsworldofart\/\">kimsworldofart<\/a> posted a photo:<\/p> <p><a href=\"http:\/\/www.flickr.com\/photos\/kimsworldofart\/31274762970\/\" title=\"Gateway Arts District Open Studio Tour, December 10, 2016\"><img src=\"http:\/\/farm1.staticflickr.com\/381\/31274762970_c40599d623_m.jpg\" width=\"240\" height=\"135\" alt=\"Gateway Arts District Open Studio Tour, December 10, 2016\" \/><\/a><\/p> <p>This photo was taken at the Otis Street Art Project in Mount Rainier, Maryland.<\/p>",
    "published": "2016-12-14T20:25:11Z",
    "author": "nobody@flickr.com (\"kimsworldofart\")",
    "author_id": "8508061@N02",
    "tags": "otisstreetartsproject gatewayartsdistrict mountrainier princegeorgescounty maryland"
}]})

如何覆盖 GSON 转换器以修剪这些额外的函数语法,然后解析剩余的有效 JSON?

【问题讨论】:

    标签: android gson jsonp retrofit2


    【解决方案1】:

    使用标准GsonConverterFactory作为指导,我们可以构造一个从流的前面删除JSONP,从而避免阅读整个内容并修剪——

    public final class GsonPConverterFactory extends Converter.Factory {
    
      Gson gson;
    
      public GsonPConverterFactory(Gson gson) {
        if (gson == null) throw new NullPointerException("gson == null");
        this.gson = gson;
      }
    
      @Override
      public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
                                                              Retrofit retrofit) {
        TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
        return new GsonPResponseBodyConverter<>(gson, adapter);
      }
    
      @Override
      public Converter<?, RequestBody> requestBodyConverter(Type type,
                                                            Annotation[] parameterAnnotations,
                                                            Annotation[] methodAnnotations,
                                                            Retrofit retrofit) {
        return null;
      }
    }
    

    和转换器主体。通过创建我们自己的 json 阅读器,我们避免了流已被完全消耗的断言。这允许我们在关闭它时将关闭的 JSONP 元素留在流中。

    final public class GsonPResponseBodyConverter<T> implements Converter<ResponseBody, T> {
      private final Gson gson;
      private final TypeAdapter<T> adapter;
    
      GsonPResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
        this.gson = gson;
        this.adapter = adapter;
      }
    
      @Override public T convert(ResponseBody value) throws IOException {
        Reader reader = value.charStream();
        int item = reader.read();
        while(item != '(' && item != -1) {
          item = reader.read();
        }
        JsonReader jsonReader = gson.newJsonReader(reader);
        try {
          return adapter.read(jsonReader);
        } finally {
          reader.close();
        }
      }
    }
    

    像常规 Gson 工厂一样添加到您的改造中 --

    Retrofit retrofit = new Retrofit.Builder()
        .baseUrl(/* you base url */)
        .addConverterFactory(new GsonPConverterFactory(new Gson()))
        .build();
    

    注意:使用此转换器将要求所有响应都使用 JSONP。它会在常规 JSON 响应中失败,并且您不能同时使用 Gson 和 GsonP 转换器。

    【讨论】:

    • 你能提供它的 Kotlin 版本吗?
    【解决方案2】:

    要解析 JSON 响应,请使用 GsonConverterFactory。

    要解析 JSONP 或 String 或无效的 JSON 响应,请使用 ScalarConverterFactory。

    如果您使用 flatMap 调用 JSON API,然后调用 JSONP API,则同时使用 GsonConverterFactory(JSON 需要)和 ScalarConverterFactory(JSONP 需要)。

    确保你的 gradle 中有以下依赖项:

    implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
    //For serialising JSONP add converter-scalars
    implementation 'com.squareup.retrofit2:converter-scalars:2.1.0'
    //An Adapter for adapting RxJava 2.x types.
    implementation 'com.squareup.retrofit2:adapter-rxjava2:2.2.0'
    

    添加converterFactories 进行改造并在构建Gson 时使用setLenient() 以消除错误JSON document was not fully consumed.

    val gson = GsonBuilder()
                .setLenient()
                .create()
    
    val retrofit = Retrofit.Builder()
                .baseUrl("http://api.flickr.com/")
                .client(builder.build())
                .addConverterFactory(ScalarsConverterFactory.create()) //important
                .addConverterFactory(GsonConverterFactory.create(gson))
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build()
    
    @GET("end-point/to/some/jsonp/url")
    fun getJsonpData() : Observable<String>
    

    使用 converters 将 JSONP 转换为 JSON,方法是删除 JSONP 中存在的前缀和后缀。 然后通过

    将字符串转换为您的数据模型
    SomeDataModel model = Gson().fromJson<SomeDataModel>(jsonResponse,
                SomeDataModel::class.java)
    

    【讨论】:

      猜你喜欢
      • 2020-04-26
      • 1970-01-01
      • 1970-01-01
      • 2020-05-14
      • 2023-03-15
      • 1970-01-01
      • 2016-03-04
      • 1970-01-01
      • 2016-01-18
      相关资源
      最近更新 更多