There's a similar question asked here, but my case is a bit different.
I'm trying to make a request similar to the following:
http://www.example.com/abc?foo=def&foo=ghi&foo=jkl&bar=xyz
I have two issues that are making things difficult. First, the repeated parameter (setting values for "foo" multiple times) is preventing the use of QueryMap
(I don't have the option to pass the values in the query string differently, like as an array). Second, the query parameters I'm using are sort of dynamic, so I can't really use Query
and supply it a list of values for a given parameter name since I won't know the parameter names until I am making the request.
The code I'm trying to upgrade from is using an older version of Retrofit, but somehow it has a concept of a QueryList
which took a List
of NameValuePair
s to pass in query parameters as the name (and their values as the value) and allowing duplicate parameters. I don't see a reference to retrofit.http.QueryList
anywhere in Retrofit's source code history or on the web, so I'm not sure if this was a custom addition at the time. In any case, I'm trying to find the best way to replicate that functionality in the latest version so any help would be appreciated!
You can pass parameter by @QueryMap Retrofit uses annotations to translate defined keys and values into appropriate format. Using the @Query("key") String value annotation will add a query parameter with name key and the respective string value to the request url .
Retrofit uses @Query annotation to define query parameters for requests. Query parameters are defined before method parameters. In annotation, we pass the query parameter name which will be appended in the URL.
Retrofit uses @Query annotation to define query parameters for requests. Query parameters are defined before method parameters. In annotation, we pass the query parameter name which will be appended in the URL. 1.1. Single or multiple query parameters Use as many @Query annotations as many parameters we want to send.
Retrofit skips null parameters and ignores them while assembling the request. Keep in mind, that you can’t pass null for primitive data types like int, float, long, etc. Instead, use Integer, Float, Long, etc and the compiler won’t be grumpy.
They also come before method parameters. They are named replacement in a URL path segment. Path parameters may not be null. Values passed in @Path annotations modified and made URL encoded before full API path resolution. 2.1. Simple path parameter An example to use @Path parameters in Retrofit 2.
Retrofit’s @QueryMap annotation is a convenient way to add multiple query parameters to a request without pre-defining their actual names. That gives you more freedom for requests by just passing the map with key-value-pairs to the service method, but doesn’t show you which options are available.
To follow-up, I was able to resolve this by using QueryMap
and a bit of a hack. Basically I turn my List
of NameValuePair
s into a HashMap where I check if I already have the key, and if I do I append the new value for the same key to the old value. So essentially (key, value) would turn into (key, value&key=value2). This way when the query string is constructed, I'll have key=value&key=value2 as desired. In order for that to work, I need to handle the value encoding myself so that the additional ampersands and equal signs that I'm including in the value don't get encoded.
So the HashMap
is constructed from the List
like this:
public static HashMap<String, String> getPathMap(List<NameValuePair> params) {
HashMap<String, String> paramMap = new HashMap<>();
String paramValue;
for (NameValuePair paramPair : params) {
if (!TextUtils.isEmpty(paramPair.getName())) {
try {
if (paramMap.containsKey(paramPair.getName())) {
// Add the duplicate key and new value onto the previous value
// so (key, value) will now look like (key, value&key=value2)
// which is a hack to work with Retrofit's QueryMap
paramValue = paramMap.get(paramPair.getName());
paramValue += "&" + paramPair.getName() + "=" + URLEncoder.encode(String.valueOf(paramPair.getValue()), "UTF-8");
} else {
// This is the first value, so directly map it
paramValue = URLEncoder.encode(String.valueOf(paramPair.getValue()), "UTF-8");
}
paramMap.put(paramPair.getName(), paramValue);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
return paramMap;
}
Then my request looks like this:
@GET("/api/")
List<Repo> listRepos(@QueryMap(encodeValues=false) Map<String, String> params);
And my service call looks like this:
// Get the list of params for the service call
ArrayList<NameValuePair> paramList = getParams();
// Convert the list into a map and make the call with it
Map<String, String> params = getPathMap(paramList);
List<Repo> repos = service.listRepos(params);
I originally tried a solution using Path
where I tried to manually construct the query string, but replacement blocks are not allowed in the query string so I went with this QueryMap solution. Hopefully this helps anyone else that runs into the same issue!
Interface:
@GET()
Call<List<CustomDto>> getCustomData(@Url String url);
Request data:
String params = "custom?";
for (Integer id : ids) {
params += "id=" + id.toString() + "&";
}
params = params.substring(0, params.length() - 1);
api.getCustomData(params).enqueue(callback);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With