如何通过 Retrofit 和 GsonConverter 处理 JSONP 响应?
How to handle JSONP response through Retrofit and GsonConverter?
我需要解析来自 Flickr API 的响应。
http://api.flickr.com/services/feeds/photos_public.gne?tagmode=any&format=json
其中 returns jsonFlickrFeed jQuery 回调函数中的响应(这不是有效的 JSON 响应)。
我知道我们可以使用 nojsoncallback=1
查询删除 Flickr API 的 JSON 回调方法。
但是,如果强制使用 JSON 和 Padding (JSONP),是否有更好的方法来处理 JSONP 响应?
不是将响应作为字符串获取,而是 trim 插入 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 Converter to trim这些额外的函数语法然后解析剩余有效的JSON?
使用标准 GsonConverterFactory
作为指南,我们可以构造一个从流的前面删除 JSONP 的,从而避免读取整个内容和 trim --
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 reader,我们避免了流已被完全消耗的断言。这允许我们在关闭流时将关闭的 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 需要)和 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>
使用转换器将JSONP转换为JSON,方法是移除JSONP中存在的前缀和后缀。
然后通过
将字符串转换为您的数据模型
SomeDataModel model = Gson().fromJson<SomeDataModel>(jsonResponse,
SomeDataModel::class.java)
我需要解析来自 Flickr API 的响应。
http://api.flickr.com/services/feeds/photos_public.gne?tagmode=any&format=json
其中 returns jsonFlickrFeed jQuery 回调函数中的响应(这不是有效的 JSON 响应)。
我知道我们可以使用 nojsoncallback=1
查询删除 Flickr API 的 JSON 回调方法。
但是,如果强制使用 JSON 和 Padding (JSONP),是否有更好的方法来处理 JSONP 响应?
不是将响应作为字符串获取,而是 trim 插入 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 Converter to trim这些额外的函数语法然后解析剩余有效的JSON?
使用标准 GsonConverterFactory
作为指南,我们可以构造一个从流的前面删除 JSONP 的,从而避免读取整个内容和 trim --
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 reader,我们避免了流已被完全消耗的断言。这允许我们在关闭流时将关闭的 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 需要)和 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>
使用转换器将JSONP转换为JSON,方法是移除JSONP中存在的前缀和后缀。 然后通过
将字符串转换为您的数据模型SomeDataModel model = Gson().fromJson<SomeDataModel>(jsonResponse,
SomeDataModel::class.java)