Reading JSON to map with JAX-RS

2019-07-26 17:33发布

问题:

I'm writing a Java EE REST application (it runs under Glassfish 4.1) and I'm having problems reading a simple key-value JSON list. I'm trying to produce and consume a JSON using a POJO class, producing works fine, but consuming is not working. Let's get to the code. The POJO class I use for produce and consume JSON is like this:

public class Transport {
  protected Integer id;
  protected String name;
  protected Map<String, String> fields;
  // ...getters and setters...
}

The REST resource import those libraries (I've put here the relevant ones):

import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataParam;

The method who produces the JSON works fine, here it is:

@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("test")
public Response testGet() {
  Transport sct = new Transport();
  sct.setId(1);
  sct.setName("test");
  HashMap<String, String> t1 = new HashMap();
  t1.put("aaa", "bbb");
  t1.put("bbb", "ccc");
  t1.put("ccc", "ddd");
  sct.setFields(t1);
  // createOkResponse is a class method that return Response type
  return createOkResponse(sct);
}

so I correctly obtain this output:

{
  "id": 1,
  "name": "test",
  "fields": {
     "aaa": "bbb",
     "ccc": "ddd",
     "bbb": "ccc"
  }
}

The method who consume the JSON, using the same POJO:

@PUT
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
@Path("test")
public Response testPut(Transport sct) {
  Map<String, String> fields = sct.getFields();
  // this code throws a NullPointerException at fields.entrySet() 
  // because fields is always null
  //for (Entry<String, String> field : fields.entrySet()) {
  //  LOG.log(Level.INFO, "*** field: {0} = {1}", new Object[]{field.getKey(), field.getValue()});
  //}
  return createOkResponse(sct);
}

Consuming the service with the same JSON above will result in an sct object with id and name properties set, but with fields property null, so the response of the method is:

{
  "id": 1,
  "name": "test",
  "fields": null
}

it's like the {"aaa": "bbb", "ccc": "ddd", "bbb": "ccc"} JSON object is being ignored. I've tried googling around but I don't understand what is wrong with my code, and I've seen no errors or warnings in the glassfish log. Any idea? Thanks.

回答1:

You can use an XmlAdapter, as mentioned in this post. The problem is the way that MOXy handles Maps. It uses JAXB under the hood, which was meant to be used for XML, and XML doesn't map very well to Maps (or key/value) pairs.

For this reason, instead of using MOXy for JSON (which is the default JSON provider in Glassfish), I would recommend using Jackson. Jackson should also be included in Glassfish 4.1. But you will need to disable MOXy, so the Jackson can register itself. To disable MOXy, just set the Jersey property jersey.config.server.disableMoxyJson to true.