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!

Original source

Related problems