I have a back-end server that works like this
"api/videos?search_object={"cat_id" :2, "channel_id" : 3, etc}
Basily you can give a search object as input and it will filter the list base on that object. Now I want to use this service with Retrofit with something like this
@GET("videos")
Call<VideoListResponse> listVideos(@Query("search_object") VideoSearchObject videoSearchObject);
But the above code doesn't work, I can first convert VideoSearchModel to JSON string that pass it to retrofit like this
@GET("videos")
Call<VideoListResponse> listVideos(@Query("search_object") String jsonString);
I wonder if there is a better more clear way? Any suggestions will be appreciated.
Retrofit 2 supports it. All you have to do is implementing a custom converter factory with the stringConverter()
method overridden.
Consider the following Retrofit-friendly interface with a custom annotation:
@Target(PARAMETER)
@Retention(RUNTIME)
@interface ToJson {
}
interface IService {
@GET("api/videos")
Call<Void> get(
@ToJson @Query("X") Map<String, Object> request
);
}
The annotation is used to denote an argument that must be converted to a string.
Mock OkHttpClient
to always respond with "HTTP 200 OK" and dump request URLs:
private static final OkHttpClient mockHttpClient = new OkHttpClient.Builder()
.addInterceptor(chain -> {
System.out.println(chain.request().url());
return new Response.Builder()
.request(chain.request())
.protocol(HTTP_1_0)
.code(HTTP_OK)
.body(ResponseBody.create(MediaType.parse("application/json"), "OK"))
.build();
})
.build();
private static final Gson gson = new Gson();
private static final Retrofit retrofit = new Retrofit.Builder()
.client(mockHttpClient)
.baseUrl("http://whatever")
.addConverterFactory(new Converter.Factory() {
@Override
public Converter<?, String> stringConverter(final Type type, final Annotation[] annotations, final Retrofit retrofit) {
if ( !hasToJson(annotations) ) {
return super.stringConverter(type, annotations, retrofit);
}
return value -> gson.toJson(value, type);
}
private boolean hasToJson(final Annotation[] annotations) {
for ( final Annotation annotation : annotations ) {
if ( annotation instanceof ToJson ) {
return true;
}
}
return false;
}
})
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
To test it you can simply invoke the service interface method:
final IService service = retrofit.create(IService.class);
service.get(ImmutableMap.of("k1", "v1", "k2", "v2")).execute();
Result:
http://whatever/api/videos?X={%22k1%22:%22v1%22,%22k2%22:%22v2%22}
Where the X
parameter argument is an encoded representation of {"k1":"v1","k2":"v2"}
.