Customize Json Output with Jersey and Jaxb - java

I am trying to create a simple web service which outputs using json, but am not getting the desired Json output.
POJO:
package com.rest.resource;
import java.io.Serializable;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Track implements Serializable
{
#XmlElement
String singer = "ABC";
#XmlElement
String title = "XYZ";
}
Service:
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.xml.bind.JAXBException;
import com.rest.resource.Track;
#Path("/json/metallica")
public class JSONService
{
#POST
#Path("/post")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Track createTrackInJSON(final Track track)
{
return track;
}
#GET
#Path("/get")
#Produces(MediaType.APPLICATION_JSON)
public Response getTrackInJSON() throws JAXBException
{
final Track track = new Track();
return Response.status(201).entity(track).build();
}
}
On /get I get
{"singer":"ABC","title":"XYZ"}
but I want "track": {"singer":"ABC","title":"XYZ"}
I am unable yo print the root element.
I tried using a CustomJAXBContextResolver class but did not work for me? Can anyone give an example of the same?

If you want to use the ContextResolver, you'd need to use the JSONConfiguration and switch the JSON Notation. You could do that by adding a class like this:
#Provider
public class MyJAXBContextProvider implements ContextResolver<JAXBContext> {
private JSONJAXBContext trackCtx;
public MyJAXBContextProvider() throws JAXBException {
trackCtx = new JSONJAXBContext(JSONConfiguration.mappedJettison().build(), Track.class);
}
public JAXBContext getContext(Class<?> type) {
if(type == Track.class) {
return trackCtx;
}
return null;
}
}
Adding that class produced this for me:
{"track":{"singer":"ABC","title":"XYZ"}}
For more info check out the Jersey Docs

You'd have to wrap Track with another object:
public class TrackWrapper {
Track track;
TrackWrapper(Track track) {
this.track=track;
}
}
and return an instance of TrackWrapper,
#GET
#Path("/get")
#Produces(MediaType.APPLICATION_JSON)
public Response getTrackInJSON() throws JAXBException
{
final TrackWrapper trackWrapper = new TrackWrapper(new Track());
return Response.status(201).entity(trackWrapper).build();
}
}
and just in case, if you're gonna use JSON only you don't need the JAXB annotations.

Related

Rest API cant't return ArrayList of Object which have composition of other object

My POJO Classes are like this
1.
/*crating MesageModel like this **/
/** ==========================================================================**/
package tech.sach.webapp.webserve.subpack;
import javax.xml.bind.annotation.XmlRootElement;
import tech.sach.webapp.webserve.link.Link;
#XmlRootElement
public class MessageModel{
int mesageid;
String mesgForm;
String name;
Link links; //id i comment this it works fine
public MessageModel() {
}
public MessageModel(int id,String a,String b,Link link )
{
this.mesageid=id;
this.mesgForm=a;
this.name=b;
this.links=link;
}
public MessageModel(int id,String a,String b )
{
this.mesageid=id;
this.mesgForm=a;
this.name=b;
}
public int getMesageid() {
return mesageid;
}
public void setMesageid(int mesageid) {
this.mesageid = mesageid;
}
public String getMesgForm() {
return mesgForm;
}
public void setMesgForm(String mesgForm) {
this.mesgForm = mesgForm;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Link getLinks() {
return links;
}
public void setLinks(Link links) {
this.links = links;
}
}
2.My second POJO class
/*Link is the object used in MessageModel */
package tech.sach.webapp.webserve.link;
public class Link {
String url;
String rel;
public Link(String url,String rel) {
this.url=url;
this.rel=rel;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getRel() {
return rel;
}
public void setRel(String rel) {
this.rel = rel;
}
}
And I am trying to send response like this using Rest JAX-RS API like this.
Below is the actual rest resource i am trying to call.
Return Arraylist
package tech.sach.webapp.webserve.subpack;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import javax.inject.Scope;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriInfo;
import javax.xml.bind.annotation.XmlRootElement;
import tech.sach.webapp.webserve.link.Link;
#Path("subpack")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public class SubpackageClassCall {
/*Creating arraylist*/
ArrayList<MessageModel> aArr=new ArrayList<MessageModel>();
/* Getting response*/
#GET
public ArrayList<MessageModel> getSubpack()
{
//Creating arraylikst here with dynamic MessageModel object
aArr.add(new MessageModel(1, "sac", "sdf"));
aArr.add(new MessageModel(2, "adsdsac", "csxcxcdf"));
aArr.add(new MessageModel(3, "sadasdcxcxcsac", "swwwwwdf"));
return aArr;
}
}
I am trying to call "http://localhost:8080/webserve/webapi/subpack" in postman or browser and I am getting following error
**"SEVERE: MessageBodyWriter not found for media type=application/json, type=class java.util.ArrayList, genericType=java.util.ArrayList<tech.sach.webapp.webserve.subpack.MessageModel>."**
So in 'SubpackageClassCall' class I am creating 'MessageModel' object dynamically but I am not doing anything related to its composed object 'Link'.
If I remove that composed 'Link' object from 'MessageModel' then its working fine.
But Why its not working when I include 'Link' object in 'MessageModel' ?

java requestmapping returns 404 error with Postman

Im trying to have java respond to a GET request from Postman. When I use Postman to send a GET request to localhost:8080/chat this is the response (I had expected to get an empty list returned as there is no data present yet:
{
"timestamp": "2018-04-02T20:00:26.413+0000",
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/chat"
}
I have 2 packages in my application. They are com.dogo and com.dogochat.chat. The file in com.dogo is DogoApplication.java. This is the code:
package com.dogo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class DogoApplication {
public static void main(String[] args) {
SpringApplication.run(DogoApplication.class, args);
}
}
The second package is called com.dogochat.chat. There are 3 files (2 classes and 1 interface). The file names are Message.java, MessageController.java, and MessageRepository.java.
This is the code in Message.java:
package com.dogochat.chat;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name="message")
public class Message {
#Id
#GeneratedValue
private Integer id;
private String name;
private String content;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
This is the code in MessageController.java:
package com.dogochat.chat;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
#RequestMapping("/chat")
public class MessageController {
#Autowired
MessageRepository dao;
#GetMapping("/get")
public List<Message> getMessages(){
List<Message> foundMessages = dao.findAll();
return foundMessages;
}
#PostMapping("/post")
public ResponseEntity<Message> postMessage(#RequestBody Message message)
{
// saving to DB using instance of the repo interface
Message createdMessage = dao.save(message);
// RespEntity crafts response to include correct status codes.
return ResponseEntity.ok(createdMessage);
}
}
This is the code in MessageRepository.java (although I dont think this is needed for this small test)
package com.dogochat.chat;
import org.springframework.data.jpa.repository.JpaRepository;
public interface MessageRepository extends JpaRepository<Message, Integer>{
}
Any help/suggestions are greatly appreciated.
Thanks.
I know I am pretty late for the response, but better late than never.
This is a very common mistake for new Spring boot developers and I have also faced this issue several times.
The reason for this is the package under which you have your main() method.
In your case your MessageController, Message and your MessageRepository is under the package com.dogochat.chat, but then your main() method is under com.dogo which is completely a different package.
#SpringBootApplication internally runs #ComponentScan and if parent package and child packages are different, it cannot run and scan and throws the above error.
To avoid this confusion, follow this package structure.
Hope this helps. Happy coding !
Check to see if your #SpringBootApplication class is on top of all your packages or at least in the same package. It seems like your URL path is not visible.
Also, in your PostMan, you have to configure your headers the content-type=application/json.
For a more readable code (just my point of view), you should have something like this :
package com.dogochat.chat;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
#RequestMapping("/chat")
public class MessageController {
#Autowired
MessageRepository dao;
#GetMapping("/get")
public List<Message> getMessages(){
List<Message> foundMessages = dao.findAll();
return foundMessages;
}
#PostMapping("/post")
public ResponseEntity<Message> postMessage(#RequestBody Message message)
{
// saving to DB using instance of the repo interface
Message createdMessage = dao.save(message);
// RespEntity crafts response to include correct status codes.
return ResponseEntity.ok(createdMessage);
}
}

Implement generic abstract jaxrs service

I'm struggling on implementing a generic abstract jaxrs service without duplicating several jaxrs annotations.
So, for example, here is my service and entity structure:
AbstractEntity.java
import javax.xml.bind.annotation.XmlElement;
public abstract class AbstractEntity {
#XmlElement
private String name;
public String getName() {
return name;
}
}
AbstractService.java
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
public class AbstractService<T extends AbstractEntity> {
#POST
#Consumes(MediaType.APPLICATION_XML)
#Produces(MediaType.TEXT_PLAIN)
#Path("/details")
public Response getEntityDetails(final T entity) {
// just return the name of this entity
return Response.ok(entity.getName()).build();
}
}
The implementation is like:
Car.java
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class Car extends AbstractEntity {
#XmlElement
private String brand;
public String getBrand() {
return brand;
}
}
CarService.java
import javax.ws.rs.Path;
#Path("/cars")
public class CarService extends AbstractService<Car> {
// should provide the super getEntityDetails method with a Car entity
}
Now i want to POST my car entity to /cars/details to get the details (return the name "A5" which is implemented in the abstract service):
POST /cars/details
<car>
<brand>Audi</brand>
<name>A5</name>
</car>
Unfortunately, when I post it to my service, it says:
JAXRSUtils W .No message body reader found for request class : AbstractEntity, ContentType : application/xml.
WebApplicatio W WebApplicationException has been caught : no cause is available
I can correct it, if I implement my CarService as follows:
CarService.java
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
#Path("/cars")
public class CarService extends AbstractService<Car> {
#POST
#Consumes(MediaType.APPLICATION_XML)
#Produces(MediaType.TEXT_PLAIN)
#Path("/details")
#Override
public Response getEntityDetails(final Car entity) {
return super.getEntityDetails(entity);
}
}
And removing all jaxrs annotations from the abstract service:
AbstractService.java
import javax.ws.rs.core.Response;
public class AbstractService<T extends AbstractEntity> {
public Response getEntityDetails(final T entity) {
// just return the name of this entity
return Response.ok(entity.getName()).build();
}
}
The point is, that I have about 60 of these CarService implementations and I don't want to repeat the getDetails method with all it's jaxrs annotation in each service, because it's always the same (the same boilerplate).
Any ideas or solutions on that?
potentially you may want to also have a look at more resource-oriented rest libraries to complement your JAX-RS codebase. They can do exactly that (path mappings, sorting, filtering, paging, etc.) without an annotation or abstract base class hell. crnk.io + jsonapi, graphql, falcor are such libraries. (disclaimer: I contribute to the first one)

Error Handling that give JSON

I'm trying to create a simple error handling project, that will give JSON with error data after receiving an error (for example 404, 422 or 500). I work with code from this site, but it's not working for me.
I actually have this two classes:
BasicController class
package com.mycompany.jsonerrorhandler;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.ModelAndView;
/**
* Class to catch all exception
*/
public class BasicController
{
#ExceptionHandler (Exception.class)
#ResponseStatus (HttpStatus.INTERNAL_SERVER_ERROR)
public ModelAndView handleAllExceptions(Exception ex)
{
return new JsonError(ex.getMessage()).asModelAndView();
}
}
JsonError class
package com.mycompany.jsonerrorhandler;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.json.MappingJacksonJsonView;
import com.google.common.collect.ImmutableMap;
/**
* Class that defines what JSON Error looks like
*/
public class JsonError
{
private final String message;
public JsonError(String message)
{
this.message = message;
}
public ModelAndView asModelAndView()
{
MappingJacksonJsonView jsonView = new MappingJacksonJsonView();
return new ModelAndView(jsonView, ImmutableMap.of("error", message));
}
}
I wonder what I need to connect them and receive JSON (or maybe there is other solution for this problem).
Based on the like you provided, the JsonError class should contain the following:
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.json.MappingJacksonJsonView;
import com.google.common.collect.ImmutableMap;
public class JsonError
{
private final String message;
public JsonError(String message) {
this.message = message;
}
public ModelAndView asModelAndView() {
MappingJacksonJsonView jsonView = new MappingJacksonJsonView();
return new ModelAndView(jsonView, ImmutableMap.of("error", message));
}
}

Jax-rs json pretty output

in Java when i use the
#Produces("application/json")
annotation the output is not formated into human readable form. How do i achive that?
Just for the record, if you want to enable the pretty output only for some resources you can use the #JacksonFeatures annotation on a resource method.
Here is example:
#Produces(MediaType.APPLICATION_JSON)
#JacksonFeatures(serializationEnable = { SerializationFeature.INDENT_OUTPUT })
public Bean resource() {
return new Bean();
}
This is how you can properly do conditional pretty/non-pretty json output based on presence of "pretty" in query string.
Create a PrettyFilter that implements ContainerResponseFilter, that will be executed on every request:
#Provider
public class PrettyFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext reqCtx, ContainerResponseContext respCtx) throws IOException {
UriInfo uriInfo = reqCtx.getUriInfo();
//log.info("prettyFilter: "+uriInfo.getPath());
MultivaluedMap<String, String> queryParameters = uriInfo.getQueryParameters();
if(queryParameters.containsKey("pretty")) {
ObjectWriterInjector.set(new IndentingModifier(true));
}
}
public static class IndentingModifier extends ObjectWriterModifier {
private final boolean indent;
public IndentingModifier(boolean indent) {
this.indent = indent;
}
#Override
public ObjectWriter modify(EndpointConfigBase<?> endpointConfigBase, MultivaluedMap<String, Object> multivaluedMap, Object o, ObjectWriter objectWriter, JsonGenerator jsonGenerator) throws IOException {
if(indent) jsonGenerator.useDefaultPrettyPrinter();
return objectWriter;
}
}
}
And pretty much that's it!
You will need to ensure that this class gets used by Jersey by either automated package scanning or registered manually.
Spent few hours trying to achieve that and found that no-one has published a ready-to-use solution before.
Create this class anywhere in your project. It will be loaded on deployment. Notice the .configure(SerializationConfig.Feature.INDENT_OUTPUT, true); which configures the mapper to format the output.
For Jackson 2.0 and later, replace the two .configure() lines with these:
.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false)
.configure(SerializationFeature.INDENT_OUTPUT, true);
And change your imports accordingly.
package com.secret;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import org.codehaus.jackson.map.DeserializationConfig;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
/**
*
* #author secret
*/
#Provider
#Produces(MediaType.APPLICATION_JSON)
public class JacksonContextResolver implements ContextResolver<ObjectMapper> {
private ObjectMapper objectMapper;
public JacksonContextResolver() throws Exception {
this.objectMapper = new ObjectMapper();
this.objectMapper
.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.configure(SerializationConfig.Feature.INDENT_OUTPUT, true);
}
#Override
public ObjectMapper getContext(Class<?> objectType) {
return objectMapper;
}
}
Bear in mind that formatting has a negative effect on performance.
If you are using Spring, then you can globally set the property
spring.jackson.serialization.INDENT_OUTPUT=true
More info at https://docs.spring.io/spring-boot/docs/current/reference/html/howto-properties-and-configuration.html
Building on helpful DaTroop's answer, here is another version which allows choosing between optimized json and formatted json based on the absence or presence of a "pretty" parameter :
package test;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.SerializationConfig;
#Provider
#Produces(MediaType.APPLICATION_JSON)
public class JacksonContextResolver implements ContextResolver<ObjectMapper> {
private ObjectMapper prettyPrintObjectMapper;
private UriInfo uriInfoContext;
public JacksonContextResolver(#Context UriInfo uriInfoContext) throws Exception {
this.uriInfoContext = uriInfoContext;
this.prettyPrintObjectMapper = new ObjectMapper();
this.prettyPrintObjectMapper.configure(SerializationConfig.Feature.INDENT_OUTPUT, true);
}
#Override
public ObjectMapper getContext(Class<?> objectType) {
try {
MultivaluedMap<String, String> queryParameters = uriInfoContext.getQueryParameters();
if(queryParameters.containsKey("pretty")) {
return prettyPrintObjectMapper;
}
} catch(Exception e) {
// protect from invalid access to uriInfoContext.getQueryParameters()
}
return null; // use default mapper
}
}
If you are using the jersey-media-json-binding dependency, which uses Yasson (the official RI of JSR-367) and JAVAX-JSON, you can introduce pretty printing as follows:
import javax.json.bind.Jsonb;
import javax.json.bind.JsonbBuilder;
import javax.json.bind.JsonbConfig;
import javax.ws.rs.ext.ContextResolver;
import javax.ws.rs.ext.Provider;
#Provider
public class RandomConfig implements ContextResolver<Jsonb> {
private final Jsonb jsonb = JsonbBuilder.create(new JsonbConfig().withFormatting(true));
public RandomConfig() { }
#Override
public Jsonb getContext(Class<?> objectType) {
return jsonb;
}
}
Alternative for Jersey 1.x:
org.codehaus.jackson.map.ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationConfig.Feature.INDENT_OUTPUT);

Categories

Resources