New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Is it possible to use query string arrays - e.g. a[]=2&a[]=3&a[]=4 #472

Closed
benclayton opened this Issue May 6, 2014 · 19 comments

Comments

@benclayton

benclayton commented May 6, 2014

A server for a project I've been working on uses the 'query array' syntax to pass an array of items in for a single named query key.

E.g. a = {"2","3","4} can be passed on the query string as a[]=2&a[]=3&a[]=4.

It looks like there's no way of doing this via the QueryMap function. Is there an alternative?

@benclayton benclayton changed the title from Is it possible to use query arrays - e.g. a[]=2&a[]=3&a[]=4 to Is it possible to use query string arrays - e.g. a[]=2&a[]=3&a[]=4 May 6, 2014

@JakeWharton

This comment has been minimized.

Show comment
Hide comment
@JakeWharton

JakeWharton May 7, 2014

Collaborator

The non-Map-based annotations support both List and array types for repetition. From @Query:

@GET("/list")
void list(@Query("category") String... categories);

Calling with foo.list("bar", "baz") yields /list?category=foo&category=bar.

Just use a[] as the key.

Collaborator

JakeWharton commented May 7, 2014

The non-Map-based annotations support both List and array types for repetition. From @Query:

@GET("/list")
void list(@Query("category") String... categories);

Calling with foo.list("bar", "baz") yields /list?category=foo&category=bar.

Just use a[] as the key.

@JakeWharton JakeWharton closed this May 7, 2014

@benclayton

This comment has been minimized.

Show comment
Hide comment
@benclayton

benclayton May 7, 2014

Thank you!—
Sent from Mailbox

On Wed, May 7, 2014 at 5:52 AM, Jake Wharton notifications@github.com
wrote:

The non-Map-based annotations support both List and array types for repetition. From [@Query][1]:

@get("/list")
void list(@query("category") String... categories);

Calling with foo.list("bar", "baz") yields /list?category=foo&category=bar.
Just use a[] as the key.

[1]: http://square.github.io/retrofit/javadoc/retrofit/http/Query.html

Reply to this email directly or view it on GitHub:
#472 (comment)

benclayton commented May 7, 2014

Thank you!—
Sent from Mailbox

On Wed, May 7, 2014 at 5:52 AM, Jake Wharton notifications@github.com
wrote:

The non-Map-based annotations support both List and array types for repetition. From [@Query][1]:

@get("/list")
void list(@query("category") String... categories);

Calling with foo.list("bar", "baz") yields /list?category=foo&category=bar.
Just use a[] as the key.

[1]: http://square.github.io/retrofit/javadoc/retrofit/http/Query.html

Reply to this email directly or view it on GitHub:
#472 (comment)

@hhoang

This comment has been minimized.

Show comment
Hide comment
@hhoang

hhoang Jun 4, 2014

How would I use this if I also wanted to pass in a callback to the foo.list method?
Since retrofit callbacks and Varargs both need to be the last parameter in a method signature?

hhoang commented Jun 4, 2014

How would I use this if I also wanted to pass in a callback to the foo.list method?
Since retrofit callbacks and Varargs both need to be the last parameter in a method signature?

@JakeWharton

This comment has been minimized.

Show comment
Hide comment
@JakeWharton

JakeWharton Jun 4, 2014

Collaborator

You can use a List, array, or varags (which is just an array).

Collaborator

JakeWharton commented Jun 4, 2014

You can use a List, array, or varags (which is just an array).

@hhoang

This comment has been minimized.

Show comment
Hide comment
@hhoang

hhoang Jun 4, 2014

Thanks for the quick reply. Sorry to bother you again, but i'm having trouble with passing a list in as you suggested.

When I use a list, it seems to be formatting my url incorrectly.
I need my URL to be formatted like this:

assignment_ids[]=123&assignment_ids[]=234&assignment_ids[]=456

instead when I try to pass a list to an EncodedQuery, I'm getting this:

assignment_ids[]=[123, 234, 456]

Here's the method I'm using. I've tried both Query and EncodedQuery with similar results.

@GET("/assignments")
    void getAssignments(@Query("assignment_ids[]") List<Long> assignments, Callback<Assignment[]> callback);

I've also tried creating a custom class and overriding the toString method.

public static class AssignmentID{
        private long id;

        public AssignmentID(long id){
            this.id = id;
        }
        @Override
        public String toString() {
            return "assignment_ids[]="+String.valueOf(id);
        }
    }

Which results in

assignment_ids[]=[assignment_ids[]=234&assignment_ids[]=456]

Which also isn't quite what I want.
Can you see what i'm doing wrong? I'll keep trying other things. Thanks!

hhoang commented Jun 4, 2014

Thanks for the quick reply. Sorry to bother you again, but i'm having trouble with passing a list in as you suggested.

When I use a list, it seems to be formatting my url incorrectly.
I need my URL to be formatted like this:

assignment_ids[]=123&assignment_ids[]=234&assignment_ids[]=456

instead when I try to pass a list to an EncodedQuery, I'm getting this:

assignment_ids[]=[123, 234, 456]

Here's the method I'm using. I've tried both Query and EncodedQuery with similar results.

@GET("/assignments")
    void getAssignments(@Query("assignment_ids[]") List<Long> assignments, Callback<Assignment[]> callback);

I've also tried creating a custom class and overriding the toString method.

public static class AssignmentID{
        private long id;

        public AssignmentID(long id){
            this.id = id;
        }
        @Override
        public String toString() {
            return "assignment_ids[]="+String.valueOf(id);
        }
    }

Which results in

assignment_ids[]=[assignment_ids[]=234&assignment_ids[]=456]

Which also isn't quite what I want.
Can you see what i'm doing wrong? I'll keep trying other things. Thanks!

@JakeWharton

This comment has been minimized.

Show comment
Hide comment
@JakeWharton

JakeWharton Jun 4, 2014

Collaborator

I can't reproduce.

With this diff:

diff --git a/retrofit-samples/github-client/src/main/java/com/example/retrofit/GitHubClient.java b/retrofit-samples/github-client/src/main/java/com/example/retrofit/GitHubClient.java
index f71a10b..a8f903e 100644
--- a/retrofit-samples/github-client/src/main/java/com/example/retrofit/GitHubClient.java
+++ b/retrofit-samples/github-client/src/main/java/com/example/retrofit/GitHubClient.java
@@ -15,8 +15,10 @@
  */
 package com.example.retrofit;

+import java.util.Arrays;
 import java.util.List;
 import retrofit.RestAdapter;
+import retrofit.http.EncodedQuery;
 import retrofit.http.GET;
 import retrofit.http.Path;

@@ -32,7 +34,8 @@ public class GitHubClient {
     @GET("/repos/{owner}/{repo}/contributors")
     List<Contributor> contributors(
         @Path("owner") String owner,
-        @Path("repo") String repo
+        @Path("repo") String repo,
+        @EncodedQuery("foo[]") List<String> foos
     );
   }

@@ -40,13 +43,14 @@ public class GitHubClient {
     // Create a very simple REST adapter which points the GitHub API endpoint.
     RestAdapter restAdapter = new RestAdapter.Builder()
         .setEndpoint(API_URL)
+        .setLogLevel(RestAdapter.LogLevel.BASIC)
         .build();

     // Create an instance of our GitHub API interface.
     GitHub github = restAdapter.create(GitHub.class);

     // Fetch and print a list of the contributors to this library.
-    List<Contributor> contributors = github.contributors("square", "retrofit");
+    List<Contributor> contributors = github.contributors("square", "retrofit", Arrays.asList("bar", "baz"));
     for (Contributor contributor : contributors) {
       System.out.println(contributor.login + " (" + contributor.contributions + ")");
     }

I get the following:

---> HTTP GET https://api.github.com/repos/square/retrofit/contributors?foo[]=bar&foo[]=baz
<--- HTTP 200 https://api.github.com/repos/square/retrofit/contributors?foo[]=bar&foo[]=baz (1580ms)
Collaborator

JakeWharton commented Jun 4, 2014

I can't reproduce.

With this diff:

diff --git a/retrofit-samples/github-client/src/main/java/com/example/retrofit/GitHubClient.java b/retrofit-samples/github-client/src/main/java/com/example/retrofit/GitHubClient.java
index f71a10b..a8f903e 100644
--- a/retrofit-samples/github-client/src/main/java/com/example/retrofit/GitHubClient.java
+++ b/retrofit-samples/github-client/src/main/java/com/example/retrofit/GitHubClient.java
@@ -15,8 +15,10 @@
  */
 package com.example.retrofit;

+import java.util.Arrays;
 import java.util.List;
 import retrofit.RestAdapter;
+import retrofit.http.EncodedQuery;
 import retrofit.http.GET;
 import retrofit.http.Path;

@@ -32,7 +34,8 @@ public class GitHubClient {
     @GET("/repos/{owner}/{repo}/contributors")
     List<Contributor> contributors(
         @Path("owner") String owner,
-        @Path("repo") String repo
+        @Path("repo") String repo,
+        @EncodedQuery("foo[]") List<String> foos
     );
   }

@@ -40,13 +43,14 @@ public class GitHubClient {
     // Create a very simple REST adapter which points the GitHub API endpoint.
     RestAdapter restAdapter = new RestAdapter.Builder()
         .setEndpoint(API_URL)
+        .setLogLevel(RestAdapter.LogLevel.BASIC)
         .build();

     // Create an instance of our GitHub API interface.
     GitHub github = restAdapter.create(GitHub.class);

     // Fetch and print a list of the contributors to this library.
-    List<Contributor> contributors = github.contributors("square", "retrofit");
+    List<Contributor> contributors = github.contributors("square", "retrofit", Arrays.asList("bar", "baz"));
     for (Contributor contributor : contributors) {
       System.out.println(contributor.login + " (" + contributor.contributions + ")");
     }

I get the following:

---> HTTP GET https://api.github.com/repos/square/retrofit/contributors?foo[]=bar&foo[]=baz
<--- HTTP 200 https://api.github.com/repos/square/retrofit/contributors?foo[]=bar&foo[]=baz (1580ms)
@hhoang

This comment has been minimized.

Show comment
Hide comment
@hhoang

hhoang Jun 4, 2014

Okay, thank you.
It seems that I was running an older version of retrofit and updating to 1.5.1 fixed the issue.

Thanks again for the help, I appreciate it!

hhoang commented Jun 4, 2014

Okay, thank you.
It seems that I was running an older version of retrofit and updating to 1.5.1 fixed the issue.

Thanks again for the help, I appreciate it!

@smaspe

This comment has been minimized.

Show comment
Hide comment
@smaspe

smaspe Apr 1, 2015

Any particular reason why this is limited to

The non-Map-based annotations

?

smaspe commented Apr 1, 2015

Any particular reason why this is limited to

The non-Map-based annotations

?

@JakeWharton

This comment has been minimized.

Show comment
Hide comment
@JakeWharton

JakeWharton Apr 1, 2015

Collaborator

See #626

Collaborator

JakeWharton commented Apr 1, 2015

See #626

@smaspe

This comment has been minimized.

Show comment
Hide comment
@smaspe

smaspe commented Apr 1, 2015

thanks

@alexnum

This comment has been minimized.

Show comment
Hide comment
@alexnum

alexnum Aug 21, 2015

Thanks a lot, i saved my task :)

alexnum commented Aug 21, 2015

Thanks a lot, i saved my task :)

@sudeeps-r

This comment has been minimized.

Show comment
Hide comment
@sudeeps-r

sudeeps-r Jan 17, 2016

@JakeWharton Can you please tell me how i can pass array in GET request

Format http://www.xyz.com?ids=["1","2"];

sudeeps-r commented Jan 17, 2016

@JakeWharton Can you please tell me how i can pass array in GET request

Format http://www.xyz.com?ids=["1","2"];

@NightlyNexus

This comment has been minimized.

Show comment
Hide comment
@NightlyNexus

NightlyNexus Jan 29, 2016

Contributor

@sudeeps-r Arrays.toString(strArr) should give you that exact format. Just pass that as the value for the parameter.

Contributor

NightlyNexus commented Jan 29, 2016

@sudeeps-r Arrays.toString(strArr) should give you that exact format. Just pass that as the value for the parameter.

@egpayawal

This comment has been minimized.

Show comment
Hide comment
@egpayawal

egpayawal Jun 22, 2016

Hi Guys,
How do you make this sample query on 2.0?
sample:
checkeditems[0][studentid]=249&checkeditems[0][checkvalue]=2&checkeditems[0][remarks]=sample remarks

and

attachfileuri[0]=http://www.planwallpaper.com/static/images/wallpaper-of-love.jpg

Thanks.

egpayawal commented Jun 22, 2016

Hi Guys,
How do you make this sample query on 2.0?
sample:
checkeditems[0][studentid]=249&checkeditems[0][checkvalue]=2&checkeditems[0][remarks]=sample remarks

and

attachfileuri[0]=http://www.planwallpaper.com/static/images/wallpaper-of-love.jpg

Thanks.

@Kolyall

This comment has been minimized.

Show comment
Hide comment
@Kolyall

Kolyall Oct 4, 2016

@egpayawal did you find solution? for
sample:
checkeditems[0][studentid]=249&checkeditems[0][checkvalue]=2&checkeditems[0][remarks]=sample

Kolyall commented Oct 4, 2016

@egpayawal did you find solution? for
sample:
checkeditems[0][studentid]=249&checkeditems[0][checkvalue]=2&checkeditems[0][remarks]=sample

@Mark-William-Schumacher

This comment has been minimized.

Show comment
Hide comment
@Mark-William-Schumacher

Mark-William-Schumacher Nov 13, 2016

@egpayawal @Kolyall and all others who may be looking for a possible solution to using Query string arrays with retrofit. To make that sample query you have listed above define your api call like so:

@post("/api/blah")
public void apiCallMethod( @FieldMap Map<String,String> fields );

Now in the calling code for the apiCallMethod before making the request map the code like so...

   Map<String,String> fields = new HashMap<>();
    for (int i=0;i<users.size();i++) {
        User user= users.get(i);
        fields.put("user["+i+"][email]",user.email);
        fields.put("user["+i+"][password]",user.password);
    } 

Seems a little hacky, but it works.
Credit goes to liangroger2007 on this http://stackoverflow.com/questions/27267593/how-to-post-array-parameters-with-retrofit

Mark-William-Schumacher commented Nov 13, 2016

@egpayawal @Kolyall and all others who may be looking for a possible solution to using Query string arrays with retrofit. To make that sample query you have listed above define your api call like so:

@post("/api/blah")
public void apiCallMethod( @FieldMap Map<String,String> fields );

Now in the calling code for the apiCallMethod before making the request map the code like so...

   Map<String,String> fields = new HashMap<>();
    for (int i=0;i<users.size();i++) {
        User user= users.get(i);
        fields.put("user["+i+"][email]",user.email);
        fields.put("user["+i+"][password]",user.password);
    } 

Seems a little hacky, but it works.
Credit goes to liangroger2007 on this http://stackoverflow.com/questions/27267593/how-to-post-array-parameters-with-retrofit

@Philipp91

This comment has been minimized.

Show comment
Hide comment
@Philipp91

Philipp91 Apr 25, 2017

To avoid this assignment_ids[]=[123, 234, 456] and get the desired assignment_ids[]=123&assignment_ids[]=234&assignment_ids[]=456, you can alternatively pass in a multi map that implements java.util.Map.entrySet() to return a set with the desired entries of type Entry<String, String>. (In that case, the value type should actually be String and not Object because the same converter will be used for all items.

https://pastebin.com/NkAi63FK
Here's an implementation that takes arbitrarily nested objects (with arrays, sets, lists, ...), uses Gson to serialize them and then walks the Gson tree to construct the query string in the form of a custom Map<String, String>.

Philipp91 commented Apr 25, 2017

To avoid this assignment_ids[]=[123, 234, 456] and get the desired assignment_ids[]=123&assignment_ids[]=234&assignment_ids[]=456, you can alternatively pass in a multi map that implements java.util.Map.entrySet() to return a set with the desired entries of type Entry<String, String>. (In that case, the value type should actually be String and not Object because the same converter will be used for all items.

https://pastebin.com/NkAi63FK
Here's an implementation that takes arbitrarily nested objects (with arrays, sets, lists, ...), uses Gson to serialize them and then walks the Gson tree to construct the query string in the form of a custom Map<String, String>.

@efemoney

This comment has been minimized.

Show comment
Hide comment
@efemoney

efemoney Jul 12, 2017

Damn @Philipp91 . That is a great idea!
I needed to implement a ids[]=2&ids[]=3&ids[]=6 kind of url with over 20 distinct keys of possible arrays (So using individual @Query arguments would have been painful to say the least).

Implemented a builder that gathers all arguments but your clue was all I needed to cross the finish line. Thanks a lot. I will be implementing the equivalent of your pastebin in Java 7.

efemoney commented Jul 12, 2017

Damn @Philipp91 . That is a great idea!
I needed to implement a ids[]=2&ids[]=3&ids[]=6 kind of url with over 20 distinct keys of possible arrays (So using individual @Query arguments would have been painful to say the least).

Implemented a builder that gathers all arguments but your clue was all I needed to cross the finish line. Thanks a lot. I will be implementing the equivalent of your pastebin in Java 7.

@fakir22

This comment has been minimized.

Show comment
Hide comment
@fakir22

fakir22 Nov 23, 2017

I was facing the same issue as @efemoney and was struggling with @Philipp91 solution because I wasn't exactly sure of what to do.

So here's @Philipp91 's QueryObjectConverter for Java 7 (no lambda expression) and without the need of an injection library (@Inject) : https://pastebin.com/qVB3ZgDE

Then you can use it with something like this :

`...

ArrayList contractsParam = new ArrayList<>();
contractsParam .add("cdi");
contractsParam .add("cdd");

Map<String, List> mulitpleQueryArg = new HashMap<>();
mulitpleQueryArg.put("contract_type", contracts);

QueryObjectConverter qConverter = new QueryObjectConverter(new Gson());
Map<String,String> convertedMap = qConverter.convert(mulitpleQueryArg);

....

foo.searchJobs(convertedMap);
`

Hope this helps !

fakir22 commented Nov 23, 2017

I was facing the same issue as @efemoney and was struggling with @Philipp91 solution because I wasn't exactly sure of what to do.

So here's @Philipp91 's QueryObjectConverter for Java 7 (no lambda expression) and without the need of an injection library (@Inject) : https://pastebin.com/qVB3ZgDE

Then you can use it with something like this :

`...

ArrayList contractsParam = new ArrayList<>();
contractsParam .add("cdi");
contractsParam .add("cdd");

Map<String, List> mulitpleQueryArg = new HashMap<>();
mulitpleQueryArg.put("contract_type", contracts);

QueryObjectConverter qConverter = new QueryObjectConverter(new Gson());
Map<String,String> convertedMap = qConverter.convert(mulitpleQueryArg);

....

foo.searchJobs(convertedMap);
`

Hope this helps !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment