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
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.
Option 1
The simplest way would be to use the
@JsonCreator
annotation and supply a constructor: