title: How to serialize a POJO into query params with Jersey
body: <p>I have been playing around and creating multiple small Java RESTful client libraries for different services at my company. Most of the time, I am unable to change anything on the server side and I need to write the <a href="https://jersey.java.net" rel="nofollow">Jersey</a> pieces of code to interact with existing RESTful APIs.</p>
  
  <hr>
  
  <h1>Context</h1>
  
  <p>Up to know, I have been using Jersey with <a href="http://jackson.codehaus.org" rel="nofollow">Jackson</a> to use JSON: when I query a POJO I deserialize it from JSON, and when I need to send a POJO, I serialize it into a JSON body. This two kinds of snippets have been doing the job for me up to now...</p>
  
  <h2>Querying and de-serialization</h2>
  
  <pre><code>ClientResponse response = webResource
      .path("/path/to/resource")
      .queryParam("key", "value")
      .accept(Mediatype.APPLICATION_JSON)
      .get(ClientResponse.class);
  
  // (...) Check response status code
  
  MyClassPojo pojo = response.getEntity(MyClassPojo.class);
  </code></pre>
  
  <h2>Serialization and sending</h2>
  
  <pre><code>ClientResponse response = webResource
      .path("/path/to/resource")
      .type(Mediatype.APPLICATION_JSON_TYPE)
      .accept(Mediatype.APPLICATION_JSON)
      .post(ClientResponse.class, pojo)
  
  // (...) Check response status code
  </code></pre>
  
  <hr>
  
  <h1>Issue</h1>
  
  <p>I am now facing a RESTful server that does not accept JSON bodies for sending my POJOs. The only thing that seem to work is to use query parameters.</p>
  
  <p>For instance, if I want to send the object</p>
  
  <pre><code>public MyClassPojo {
      public int attr1;
      public String attr2;
  }
  
  MyClassPojo pojo = new MyClassPojo();
  pojo.attr1 = 42;
  pojo.attr2 = "Foo bar";
  </code></pre>
  
  <p>I would have loved to serialize it in JSON:</p>
  
  <pre><code>{
      "attr1": 42,
      "attr2": "Foo bar"
  }
  </code></pre>
  
  <p>But this specific RESTful server is expecting query params:</p>
  
  <pre><code>?attr1=42&amp;attr2=Foo+bar
  </code></pre>
  
  <hr>
  
  <h1>Question</h1>
  
  <p>This kinda sucks, but I don't really have a choice... and I am now hoping that there is an easy way to achieve this with Jersey: <strong>how can I automatically serialize objects as query parameters, to send to a RESTful server?</strong></p>
  
  <p><strong><em>Note:</strong> I closed this question as @Jukka answered it. Don't hesitate referring to a new question you create, if like me you are actually looking for a way to send x-www-form-urlencoded data. I am about to get something working...</em></p>
  
  <hr>
  
  <h1>Update</h1>
  
  <p>Following an idea from @Jukka I wrote the following method:</p>
  
  <pre><code>public MultivaluedMap&lt;String, String&gt; toQueryParams() {
      final MultivaluedMap&lt;String, String&gt; queryParams = new Form();
      final Field[] fields = getClass().getDeclaredFields();
      for (Field field : fields) {
          final boolean accessible = field.isAccessible();
          try {
              field.setAccessible(true);
              final Object value = field.get(this);
              if (value != null) {
                  final String name = field.getName();
                  queryParams.add(name, value.toString());
              }
          } catch (IllegalAccessException e) {
              LOGGER.error("Error accessing a field", e);
          } finally {
              field.setAccessible(accessible);
          }
      }
      return queryParams;
  }
  </code></pre>
  
  <p>This is a great starting point, and would work perfectly if you actually do need Query Params. In my case, I got confused and I actually need a x-www-form-urlencoded! For that purpose I had to write a MessageBodyWriter!</p>
  
  <hr>
  
  <h1>My form encoding provider</h1>
  
  <pre><code>@Produces(MediaType.APPLICATION_FORM_URLENCODED)
  public class MyFormEncodingProvider implements MessageBodyWriter&lt;Object&gt; {
      private static final String ENCODING = "UTF-8";
  
      @Override
      public boolean isWriteable(Class&lt;?&gt; aClass, Type type, Annotation[] annotations, MediaType mediaType) {
          return true;
      }
  
      @Override
      public long getSize(Object obj, Class&lt;?&gt; aClass, Type type, Annotation[] annotations, MediaType mediaType) {
          return -1;
      }
  
      @Override
      public void writeTo(Object obj, Class&lt;?&gt; aClass, Type type, Annotation[] annotations, MediaType mediaType,
                          MultivaluedMap&lt;String, Object&gt; stringObjectMultivaluedMap, OutputStream outputStream) throws IOException, WebApplicationException {
          final Writer osWriter = new OutputStreamWriter(outputStream);
          final MultivaluedMap&lt;String, String&gt; fieldsAndValues = getFieldsAndValues(obj);
  
          boolean firstVal = true;
          for (Entry&lt;String, List&lt;String&gt;&gt; entry : fieldsAndValues.entrySet()) {
              final List&lt;String&gt; values = entry.getValue();
              if (values == null || values.size() == 0) {
                  if (!firstVal) {
                      osWriter.write("&amp;");
                  }
                  osWriter.write(entry.getKey() + "=");
                  firstVal = false;
              } else {
                  for (String value : values) {
                      if (!firstVal) {
                          osWriter.write("&amp;");
                      }
                      osWriter.write(entry.getKey() + "=" + URLEncoder.encode(value, ENCODING));
                      firstVal = false;
                  }
              }
          }
          osWriter.flush();
          osWriter.close();
      }
  
      private static MultivaluedMap&lt;String, String&gt; getFieldsAndValues(Object obj) {
          // Find all available fields
          final Collection&lt;Field&gt; allFields = new ArrayList&lt;&gt;();
          Class&lt;?&gt; clazz = obj.getClass();
          while (clazz != null &amp;&amp; clazz != Object.class) {
              Collections.addAll(allFields, clazz.getDeclaredFields());
              clazz = clazz.getSuperclass();
          }
  
          // Get all non-null values
          final MultivaluedMap&lt;String, String&gt; queryParams = new Form();
          for (Field field : allFields) {
              final boolean accessible = field.isAccessible();
              try {
                  field.setAccessible(true);
                  final Object value = field.get(obj);
                  if (value != null) {
                      final String name = field.getName();
                      queryParams.add(name, value.toString());
                  }
              } catch (IllegalAccessException e) {
                  Logger.getLogger(AbstractIMSPojo.class).error("Error accessing a field", e);
              } finally {
                  field.setAccessible(accessible);
              }
          }
          return queryParams;
      }
  }
  </code></pre>