如何通过Retrofit和gsonConverter处理JSONP响应



我需要从flickr api中解析响应。
http://api.flickr.com/services/feeds/photos_public.gne?tagmode=yan& format=json

返回JSONFLICKRFEED jQuery呼叫回复函数中的响应(这不是有效的JSON响应)。

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

但是,如果必须将JSON与Padding(JSONP)使用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?

使用标准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转换器。

用于解析JSON响应,使用gsonconverterfactory。

用于解析JSONP或字符串或无效的JSON响应,请使用scalarconverterfactory。

如果您使用Say flatmap调用JSON API,然后使用JSONP API,则使用GSONConverterFactory(json所需)和scalarConconverterFactory(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'

在构建GSON时,添加转换式Factories进行改造并使用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>

使用转换器通过删除JSONP中存在的前缀和后缀来转换JSONP以获取JSON。然后通过

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

最新更新