Retrofit — Multiple query parameters of same name where name is set dynamically

Viewed 5820

I'm trying to migrate an old project to Retrofit library and this project has quite tricky API. So I have a query template like this:

@GET(value = "products/search")
Single<ProductSearchResponse> productSearch();

And I have to add some parameters here of following template:

filter[attributeId]=attributeValueId

For example:

products/search?filter[1]=10&filter[1]=11&filter[2]=20&filter[2]=21

That's how API works and I can't change it. I know that we can pass a list as a parameter, like this:

@Query("filter") List<Integer> attributeValueIds

But how can I also set parameter's name dynamically?

3 Answers

You can use an arrayList! Something like the code below.

@GET(value = "products/search")
Single<ProductSearchResponse> productSearch(
    @Query("status") List<Integer> status
);


ArrayList<Integer> queryStatus = new ArrayList<>();
queryStatus.add(0);
queryStatus.add(1);
queryStatus.add(2);

productService.productSearch(queryStatus);

Your url will be like that -> {url}?status=0&status=1&status=2

Thanks to the link, posted by @ILLIA DEREVIANKO (https://github.com/square/retrofit/issues/1324), I've managed to solve the problem with this class:

   public class ProxyRetrofitQueryMap extends HashMap<String, Object> {
    public ProxyRetrofitQueryMap(Map<String, Object> m) {
        super(m);
    }

    @Override
    public Set<Entry<String, Object>> entrySet() {
        Set<Entry<String, Object>> originSet = super.entrySet();
        Set<Entry<String, Object>> newSet = new HashSet<>();

        for (Entry<String, Object> entry : originSet) {
            String entryKey = entry.getKey();
            if (entryKey == null) {
                throw new IllegalArgumentException("Query map contained null key.");
            }
            Object entryValue = entry.getValue();
            if (entryValue == null) {
                throw new IllegalArgumentException(
                        "Query map contained null value for key '" + entryKey + "'.");
            }
            else if(entryValue instanceof List) {
                for(Object arrayValue:(List)entryValue)  {
                    if (arrayValue != null) { // Skip null values
                        Entry<String, Object> newEntry = new AbstractMap.SimpleEntry<>(entryKey, arrayValue);
                        newSet.add(newEntry);
                    }
                }
            }
            else {
                Entry<String, Object> newEntry = new AbstractMap.SimpleEntry<>(entryKey, entryValue);
                newSet.add(newEntry);
            }
        }
        return newSet;
    }
}

With this we can just use a map, where key is a unique parameter name and value is a List of Strings, that are values for this parameter. Something like this:

ProxyRetrofitQueryMap map = new ProxyRetrofitQueryMap();

    List<String> values1 = new ArrayList<>();
    values1.add("10");
    values1.add("11");
    map.put("filter[1]", values1);

    List<String> values2 = new ArrayList<>();
    values1.add("20");
    values1.add("21");
    map.put("filter[2]", values2);
Related