Adding duplicate parameters with Retrofit v1.9.0
android, retrofit
Solution
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!
Problem
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!