How to tell Jersey Jackson to not serialize one resource? - java

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.

Related

Spring Rest - deserialize nested object by Jackson

we are using javascript library which send parameters like this:
datatable[pagination][page]: 1
datatable[pagination][pages]:
datatable[pagination][perpage]: 10
datatable[sort][sort]: asc
datatable[sort][field]: title
w_id: 6453111
here is full GET url"
http://localhost:8081/foo/bar?datatable%5Bpagination%5D%5Bpage%5D=-2&datatable%5Bpagination%5D%5Bpages%5D=&datatable%5Bpagination%5D%5Bperpage%5D=20&datatable%5Bsort%5D%5Bfield%5D=title&datatable%5Bsort%5D%5Bsort%5D=desc&datatable%5Bquery%5D=&pagination%5Bpage%5D=-2&pagination%5Bpages%5D=&pagination%5Bperpage%5D=20&sort%5Bfield%5D=title&sort%5Bsort%5D=desc&query=&w_id=6453111
encoded as query parameters. We are not able to modify it, so I want write DTO object to deserialize this parameters in elegant way. Is possible to do it in Jackson?
My Dto should lookes like that:
#Getter
#Setter
public class DataDto {
private Pageable pageable;
private String wId;
}

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

Spring Data Rest validation for request with dynamic body

I am newbie to Spring related technologies.
I choose Spring Data Rest to implement a web annotation server. According to the standard, an annotation should be represented by JSON-LD, which means you can't bind the request to any of your domain object as the field names are changeable. (In C#, it is ok to bind it to dynamic). You just need to convert it to some-defined type before persisting to db.
Before converting it, I want to validate the request body.
I use:
#Service
public class AnnotationValidator implements Validator{
#Autowired
private Processor ldProcessor;
#Override
public boolean supports(Class<?> aClass) {
return AnnotationDocument.class.equals(aClass);
}
#Override
public void validate(Object o, Errors errors) {
Object processedAnnotation;
try {
processedAnnotation = ldProcessor.extractAnnotationModel(o);
} catch (JsonLdError jsonLdError) {
jsonLdError.printStackTrace();
}
}
}
In validate method, the Object o does not represent the request body. Indeed, it tries to cast the request body to AnnotationDocument, so I cannot validate it.
Finally, my question is:
How can I process the pure request body and check its fields?
I solved my problem by creating #RepositoryRestController but I think it should be simpler way.

How to bind set object to controller in spring

I am trying to make a post call to a controller, but the object I am expecting contains a Set datatype and I am unsure how the post data should look.
Models:
public class Notebook{
private string name;
private Set<Todo> todos;
}
public class Todo{
private String name;
}
Controller
#RequestMapping(method = RequestMethod.POST)
public void createNotebook(Notebook q){
questionnaireService.saveOrUpdateNotebook(q);
}
Currently I have tried posting like the example below:
curl --data "name=some notebook&todos[0].name=todo1&todos[1].name=todo2" http://localhost:8080/api/notebook
Doesn't seem to work. Anyone have experience with Sets?
You should qualify Notebook q with #RequestBody annotation so that the request can be mapped to an object of type Notebook. More about the format of the input data and the converters in Spring MVC doc: Mapping the request body with the #RequestBody annotation.
We send data from the front-end in JSON format and use Jackson JSON to convert it to the Java object. If you go that route, you can directly declare the todos as Set<String> and the input would be
{
name: "some notebook",
todos: ["todo1", "todo2"]
}

Convert JSON query parameters to objects with JAX-RS

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)

Categories