Right now we have two options for adding query parameters to the requests: @Query and @QueryMap:
@Query can be used for single parameters, but using it does not scale when a request can have a lot of different query parameters.@QueryMap can be used to convert a Map instance to query parameters, but it has the disadvantage that the interface definition does not include the names and types of all possible parameters.What I would like to propose is adding a new annotation or extending @QueryMap to support the serialization of POJOs to fix the mentioned disadvantage of the current @QueryMap approach.
Example:
class RequestParams {
String q = null;
int count = 50;
Integer start = null;
}
@GET Observable<Response> startSomeRequest(@QueryMap RequestParams params);
For annotation convention, I guess people are more familiar with the following
startSomeRequest(@QueryMap Map<K,V> params); // requires a Map of params, and just Map
than the following
startSomeRequest(@QueryMap T params); // requires a Map, but please feed it by any kind of serializable objects
So for what you want, I think a convenient method to serialize your Object into Map is a better approach. On the other hand, if you own the server, please consider to support Body as request param. It fits better for a complicated object type.
@eneim unfortunately I don't control the server. Your proposed approach of converting Object to Map still has the disadvantage mentioned above of not being explicit about the supported query parameters.
Retrofit is not going to implement this, because it's basically a pathway to becoming our own object serializer which is not something that we want to embed. Even a simple version of this would quickly devolve into the large mechanics needed for proper serialization customization. We already border on that with just the parameters and return type.
That said, there's one pathway to doing this yourself currently and one that will be added in the future:
The first is by registering a Converter.Factory which provides a stringConverter for these types. It would need to be used with @Query(encoded=true) so that things like & work properly in the final URL.
The second is with #626 which will allow custom hookups of annotations to the underlying RequestBuilder. This will allow you to create @QueryObject which actually calls addQueryParam directly for each field. This is a post-2.0 feature, however.
@JakeWharton thanks for the detailed response!
But what if you still will support this only for some interface, for example:
class RequestParams implements QueryString{
String q = null;
int count = 50;
Integer start = null;
public String getAsQueryString(){
// escaping omitted for simplicity
return "q="+q+"&count="+count+"&start="+start;
}
}
@GET Observable<Response> startSomeRequest(@QueryMap RequestParams params);
In the end, it will simplify the solution and the end-user will take care of serialization.
A good compromise to this would be to allow an interface that produces the query map.
Something like:
interface QueryPojo {
Map<String, Object> build();
}
class CustomParameters implements QueryPojo {
...
}
interface Client{
@GET("/")
Call<Response> requestWithParameters(@QueryPojo MyQueryPojo parameters);
}
You can get all the benefits of custom POJOs (type-safety, auto-completion, builders) and the Retrofit side of things can be handled exactly as @QueryMap.
Most helpful comment
A good compromise to this would be to allow an interface that produces the query map.
Something like:
You can get all the benefits of custom POJOs (type-safety, auto-completion, builders) and the Retrofit side of things can be handled exactly as
@QueryMap.