Convert JSON query parameters to objects with JAX-RS - java

I have a JAX-RS resource, which gets its paramaters as a JSON string like this:
http://some.test/aresource?query={"paramA":"value1", "paramB":"value2"}
The reason to use JSON here, is that the query object can be quite complex in real use cases.
I'd like to convert the JSON string to a Java object, dto in the example:
#GET
#Produces("text/plain")
public String getIt(#QueryParam("query") DataTransferObject dto ) {
...
}
Does JAX-RS support such a conversion from JSON passed as a query param to Java objects?

Yes, you can do this, but you will need to write the conversion code yourself. Fortunately, this is easy, you just need to write a class that has a public String constructor to do the conversion. For example:
public class JSONParam {
private DataTransferObject dto;
public JSONParam(String json) throws WebApplicationException {
try {
// convert json string DataTransferObject and set dto
}
catch (JSONException e) {
throw new WebApplicationException(Response.status(Status.BAD_REQUEST)
.entity("Couldn't parse JSON string: " + e.getMessage())
.build());
}
}
public DataTransferObject getDTO() {
return dto;
}
}
Then you can use:
#GET
#Produces("text/plain")
public String getIt(#QueryParam("query") JSONParam json) {
DataTransferObject dto = json.getDTO();
...
}

As mentioned, you do need to explicitly convert from String parameter to JSON. But there is no need to use something as primitive as org.json's parser; Jackson or Gson can do data binding (String to JSON, JSON to POJO) in a line or two. With Jackson:
MyValue value = new ObjectMapper().readValue(json, MyValue.class);
(for production code, just create ObjectMapper once as static member, reuse)
Jackson is what most JAX-RS implementations use to implement data-binding for POST data, so this is quite similar.

Adding to Jason's solution, using http://www.json.org/java/ (courtesy of Crockford):
import org.json.JSONObject;
public class JSONParam {
private DataTransferObject dto;
public JSONParam(String json) throws WebApplicationException {
try {
// convert json string DataTransferObject and set dto
JSONObject jo = new JSONObject(json);
dto.setParamA(jo.getString("paramA"));
dto.setParamB(jo.getString("paramB"));
// There are other get methods for Integer, Double, etc.
// You can also build JSON from Java objects.
}
catch (JSONException e) {
throw new WebApplicationException(Response.status(Status.BAD_REQUEST)
.entity("Couldn't parse JSON string: " + e.getMessage())
.build());
}
}
public DataTransferObject getDTO() {
return dto;
}
}
Don't re-invent the wheel :-)

JAX-RS supports the use of JAXB (Java API for XML Binding) to bind a JavaBean to XML or JSON and vise versa. More details can be found here, for example: http://www.ibm.com/developerworks/web/library/wa-aj-tomcat/index.html
You need to
Add #XmlRootElement annotation on DataTransferObject
Create it an empty default constructor in DataTransferObject
Add #Consumes(MediaType.APPLICATION_JSON) annotation to your WebService

If you're interested in generating your DTOs, can I suggest jsonschema2pojo? You can define your objects using JSON Schema and have your DTOs automatically generated.
Once you've written the schema, you can also give it to your consumers so that they understand exactly how requests should be formatted.

Maybe you could use
http://docs.spring.io/spring-framework/docs/2.5.x/api/org/springframework/beans/BeanUtils.html
BeanUtils.copyProperties(source, target)

Related

How to return response of JSONArray in Jackson framework rest api's?

#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getData(#QueryParam("id") long id) throws Exception {
JSONArray json = (getting some json data from db)
ObjectMapper obj = new ObjectMapper();
return Response.ok(obj.writeValueAsString(json)).build();
}
I am trying to return the json array like this but getting error like this.
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.json.JSONArray and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)
Can anyone help me with serialize this and provide the response. Thanks in Advance.
If you are using spring then you don't need JSONArray, just create a simple POJO class with all the getter and setter and use ObjectMapper to parse the JSON into that object, and then write the POJO class to response, as given below:
public ResponseEntity<DummyObject> getData(#QueryParam("id") long id) throws Exception
DummyPojo obj = mapper.readValue(json, DummyPojo.classs);
return ResponseEntity.ok(obj)
}
// Replace DummyObject with your class
Spring will automatically converts into json, please check this link for more info

Using Object Mapper throw an Exception when whitespace key is passed in the map

I am invoking a REST POST API with following body:
{
"ref":{" ":"123"}
}
In the backend I am using Object Mapper to deserialize the above body to a POJO object as shown below -
public class POJO{
public Map<String,String> ref;
public Map<String,String> getRef(){
return ref;
}
public void setRef(Map<String,String> map){
this.ref = map;
}
}
But I want to throw an exception when whitespace key is sent as in the above case. Currently Object mapper allows the above behavior.
I don't want to use a custom deserializer.
Is there a way using jackson annotation or using some available module which can be registered in Object mapper so that it doesn't allows the above body and throws an exception?

How to retrieve the JSON message body in JAX-RS REST method?

I have the following JSON that will be passed as part of a HTTP request, in the message body.
{
"names": [
{
"id":"<number>",
"name":"<string>",
"type":"<string>",
}
]
}
My current REST handler is below. I am able to get the Id and `Version that is passed in as path params, but I am not sure how to retrieve the contents on the message body?
#PUT
#Path("/Id/{Id}/version/{version}/addPerson")
public Response addPerson(#PathParam("Id") String Id,
#PathParam("version") String version) {
if (isNull(Id) || isEmpty(version)) {
return ResponseBuilder.badRequest().build();
}
//HOW TO RECIEVE MESSAGE BODY?
//carry out PUT request and return DTO: code not shown to keep example simple
if (dto.isSuccess()) {
return Response.ok().build();
} else {
return Response.serverError().build();
}
}
Note: I am using the JAX-RS framework.
You just need to map your name json to a POJO and add #Consumes annotation to your put method, here is an example:
#PUT
#Consumes("application/json")
#Path("/Id/{Id}/version/{version}/addPerson")
public Response addPerson(#PathParam("Id") String Id,
#PathParam("version") String version,
List<NamObj> names) {
I assume you are trying to retrieve a list of elements if is not the case just use you POJO as it in the param.
Depending on what json library are you using in your server you may need to add #xml annotation to your POJO so the parser could know how to map the request, this is how the mapping for the example json should look like:
#XmlRootElement
public class NameObj {
#XmlElement public int id;
#XmlElement public String name;
#XmlElement public String type;
}
Jersey doc: https://jersey.java.net/documentation/latest/user-guide.html#json
#cosumes reference: http://docs.oracle.com/javaee/6/tutorial/doc/gilik.html#gipyt

Serialize and deserialize an object with an abstract field using jackson

What I'd like to do seems simple, just handling an object like the following:
When receiving a Post request from a client(web browser), which has a JSON object in its body, the server deserializes it into a certain object, and serializes some of its fields then saves them into a database.
When receiving a Get request from the client, the server retrieves the object from the database, deserializes it, composes it with other information and gives it back to the client.
The problem is, the object contains an abstract field.
Saving a request works fine
When I add these annotations to the abstract class, the phase 1 works fine.
Request JSON:
{ config: { type: "concreteA", ...}, ...}
REST API:
#RequestMapping(value="/configs", method=RequestMethod.POST)
#ResponseBody
public ResponseEntity<Object> saveConfig(#RequestBody ConfigRequest request)
throws IOException {
...
}
ConfigRequest class:
public class ConfigRequest {
private AbstractConfig config;
// Abbr. other fields, and all getters and setters
}
AbstractConfig class, which is included in ConfigRequest
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME,
include=JsonTypeInfo.As.PROPERTY,
property="type",
visible=true)
#JsonSubTypes({#Type(value=ConcreteAConfig.class, name="concreteA")})
public abstract class AbstractConfig {
public AbstractConfig(){}
private String type;
// Abbr. other fields, and all getters and setters
}
Deserialized string:
{"type":"concreteA", ...}
Deserializing the json string fails
But when I try retrieving and deserializing(Phase 2), the deserialization fails:
16/03/24 17:17:20 ERROR (...Abbr...) org.codehaus.jackson.map.JsonMappingException: Can not construct instance of AbstractConfig, problem: abstract types can only be instantiated with additional type information
RowMapper, which raises the error:
public class BatchRowMapper implements RowMapper<Batch> {
private static ObjectMapper objectMapper = new ObjectMapper();
#Override
public Batch mapRow(ResultSet row, int rowNum) throws SQLException {
Batch batch = new Batch();
try {
// THIS METHOD RAISES THE ERROR
batch.setConfig(objectMapper.readValue(row.getString(CONFIG), AbstractConfig.class));
} catch (ClassCastException|IOException e) {
throw new SQLException(e.toString());
}
return batch;
}
}
I'd like to ser/de an abstract field with the same "type" field, and using only annotaions is my wish... Is it possible? If further information needed, I will be willing.
Thank you in advance.
I've found it's the problem what Jackson I used.
When I use jackson of codehause(older), deserialization with JsonTypeInfo doesn't work properly. Jackson of fasterxml works perfectly.

How to tell Jersey Jackson to not serialize one resource?

I have a REST API implemented using Jersey, I am using the Jackson feature for automatically serialize objects to JSON, there is a special case where I need to return a JSON string that represents an script. As the script can have different unknown structures I cannot just serialize it to an Object, that script comes from a column in a db table. At the same time I have a full Script object that contains all the information of the DB including the script string as a String property.
What I want is to tell Jersey-Jackson not to serialize (skip) the endpoint GET /script/{scriptId}, look at the code:
#Path("script")
#Produces(MediaType.APPLICATION_JSON)
public class ScriptResource {
private ScriptService service;
#GET
#Path("{scriptId}")
public String getScript(#NotNull #PathParam("scriptId") Integer scriptId) {
return service.getScript(scriptId); // returns a a valid JSON String
}
#GET
#Path("full/{scriptId}")
public Script getFullScript(#NotNull #PathParam("scriptId") Integer scriptId) {
return service.getFullScript(scriptId); // returns a Script object
}
}
The #Produces annotation is the one that triggers the automatic transformation via Jackson, I would like to configure Jackson and exlude the resource endpoint that I don't want to be converted automatically.
I don't want to:
Use Response as a return type
Change the Produces annotation to avoid Jackson
Use a Map as a return type which I would feed by parsing the String
One more option which you can consider in addition to those mentioned by #wasabi, is having a wrapper class for your string which would customize the Jackson serialization so it would not be converted to JSON. That can be done by using the combination of the #JsonValue and #JsonRawValue annotations on the getter method.
Here is an example wrapper:
public class RawJsonString {
private final String json;
public RawJsonString(String json) {
this.json = json;
}
#JsonRawValue
#JsonValue
public String getJson() {
return json;
}
}
... then your modified resource method would look as follows:
#GET
#Path("{scriptId}")
public JsonRawString getScript(#NotNull #PathParam("scriptId") Integer scriptId) {
return new JsonRawString(service.getScript(scriptId)); // returns a a valid JSON String
}
If you just want to make sure that some fields in Script class would not be serialized by Jackson, you could easily do so by annotating such fields with #JsonIgnore.
If you would like to have more control over the generated JSON, I would implement a custom JsonSerializer, and refer to it using #JsonSerialize annotation in your Script class. See this post for an example.
If you cannot modify Script class, you could also implement a MessageBodyWriter. See this post for an example.

Categories