skip to Main Content

In a Java REST service using JAX-RS (Jersey 3.1.1, Jackson 2.14.2), I have an immutable data object, say:

public class MyDataObj {
    private int a;
    public static MyDataObj valueOf(String jsonStr) {
        return new MyDataObj(new ObjectMapper().readTree(jsonStr).get("a").asInt());
    }
    public MyDataObj(int a) { this.a = a; }
    public int getA() { return a; }
}

I want to read it from the JSON body of a request. The example above skips error checking etc. for brevity. This is the service method which tries to read the request body:

@POST
@Path("/myService")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response myService(MyDataObj serviceBody) {
    String msg = doSomethingMeaningfulWith(serviceBody.getA());
    return Response.ok().entity("{ "result": "" + msg + "" }");
}

Calling the service with content type "application/json" and a body of { "a": 42 } fails with an HTTP 415 "Unsupported Media Type" error. The exception is
org.eclipse.persistence.exceptions.JAXBException Exception Description: The class MyDataObj requires a zero argument constructor or a specified factory method. Note that non-static inner classes do not have zero argument constructors and are not supported.

The code above does succeed if I annotate the "serviceBody" parameter either with @HeaderParam (and send the data in the request header) or with @QueryParam (sending the data in a GET request), because JAX-RS specifies that you can construct your objects from JSON yourself using a static "valueOf" method taking a single "String" as parameter, instead of using default constructors and setters.

But the "valueOf" static method will not be called in the example above.

Question is now: how can I register a "factory method" somewhere that will construct the object? Or is there some alternative way?

2

Answers


  1. Chosen as BEST ANSWER

    This is actually more a workaround than a solution, but I want to post it anyway, since it helped me for now and may help others. Feel free to post a 'real' answer if you got one.

    @POST
    @Path("/myService")
    @Consumes(MediaType.APPLICATION_JSON)
    @Produces(MediaType.APPLICATION_JSON)
    public Response myService(String serviceBodyStr) {
        MyDataObj serviceBody = MyDataObj.valueOf(serviceBodyStr);
        String msg = doSomethingMeaningfulWith(serviceBody.getA());
        return Response.ok().entity("{ "result": "" + msg + "" }");
    }
    

  2. Option 1
    The simplest way would be to use the @JsonCreator annotation and supply a constructor:

        @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
        public UserData(@JsonProperty("a") int a) {
            this.a = a
        }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search