Rest JAX-RS exception, MessageBodyWriter not found - java

I am using Jersey 2.13
I get MessageBoddyWriter not found exception when I try to access a resource via a url in a browser.
Exception:
MessageBodyWriter not found for media type=application/json,
type=class java.util.ArrayList, genericType=java.util.List<com.webservices.entity.Book>.
I have another method that produces "APPLICATION_XML" and that seems to work fine.
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Book {
#XmlElement
private String name;
#XmlElement
private String author;
//getters setters
}
#GET
#Path("/json")
#Produces(MediaType.APPLICATION_JSON)
public List<Book> getJsonResponse(JAXBElement<Book> book){
return new ArrayList<Book>();
}
My thought was jersey would automatically find the "JacksonJsonProvider", a message writer class, provided by Jackson but it doesn't.
My lib folder:

According to mkyong jersey+jackson tutorial, you need to add com.sun.jersey.api.json.POJOMappingFeature param in your web.xml to integrate them
<servlet>
<servlet-name>jersey-serlvet</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.mkyong.rest</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

Starting with Jersey 2.9, automatic discovery of converter classes has been disabled. You have to register the converter class of your JSON library with Jersey manually. Here is what I do (I'm using Genson):
#ApplicationPath( "/api/library" )
public class RestService extends Application {
#Override
public Set<Class<?>> getClasses( ) {
final Set<Class<?>> returnValue = new HashSet<Class<?>>( );
returnValue.add( GensonJsonConverter.class );
return returnValue;
}
}

If you want to make your application code support both xml and json you need to create a wrapper object to support the collection rather than returning a GenericType. The PojoMappingFeature will work as it uses the native Jackson ObjectMapper instead of using the jaxb annotations for marshalling the object.
If you want it to be portable the best solution is to create a simple wrapper object as such.
#XmlRootElement
public class Books {
private List<Book> books;
public Books() {
}
public Books(List<Book> books) {
this.books = books;
}
#XmlElement(name="book")
public List<Book> getBooks() {
return books;
}
public void setBooks(List<Book> books) {
this.books = books;
}
}
#GET
#Path("/json")
#Produces(MediaType.APPLICATION_JSON)
public Books getJsonResponse(JAXBElement<Book> book){
return new Books(new ArrayList<Book>());
}
If you decide you want to also support MediaType.APPLICATION_XML then this is required. This will also solve the json (jackson) problem.

Related

Error converting JSON to object via jersey

I've created DynamicWeb project (not maven) in eclipse with Target server Tomcat 7.
In location '\WebContent\WEB-INF\lib' I placed jars:
jersey-json-1.19.jar
jersey-bundle-1.19.jar
jersey-core-1.19.jar
in web.xml I have:
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
Pojo:
#XmlRootElement(name = "input")
public class InputOb {
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Rest resource:
#Path("/add")
public class AddOb {
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response add(InputOb input) {
....
}
}
When I try to call it, I am getting:
SEVERE: A message body reader for Java class datatypes.InputOb, and
Java type class datatypes.InputOb, and MIME media type
application/json was not found.
Try to add your dependencies through pom.xml and use maven to run. Maybe something goes wrong with the jars.

In JAX-RS, entire JSON string is being posted

I have a simple Jersey POST method that accepts JSON. I am using SOAP UI to test, using POST and media type application/JSON.
Within the request body I have JSON:
{
email:"test"
}
When I test this, the string I expect to come into the service as test actually comes in as the entire JSON string. Not sure what's wrong here, it should work according to the docs.
#POST
#Path("/TEST")
#Consumes(MediaType.APPLICATION_JSON)
public Response testJaxRs(String email){
// email = "{
// email:"test"
// }"
return Response.ok().build();
}
If you do not want to deserialize it to a bean, then you can accept the post body as a map of key-value pairs. For example:
public Response testJaxRs(Map<String, String> body) {
body.get("email") // "test"
...
You need a bean
public class Email{
private String email;
//getters&setters
}
public Response testJaxRs(Email emailBean){
Note that you need to add the dependencies to decode json. e.g Jackson and add this mapping to jersey servlet in web.xml> See full example here: https://www.mkyong.com/webservices/jax-rs/json-example-with-jersey-jackson/
<web-app ...>
<servlet>
<servlet-name>jersey-servlet</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.yourpackage</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
If you're using Jersey 1.x, then to receive your Json as an object you need to declare the POJO and annotate it with javax.xml.bind.annotation.XmlRootElement:
#XmlRootElement
public class EmailTest{
String email;
public String getEmail(){
return this.email;
}
public void setEmail(String email) {
this.email = email;
}
}
Then you have to use this class in your controller:
#POST
#Path("/TEST")
#Consumes(MediaType.APPLICATION_JSON)
public Response testJaxRs(Email email){
System.out.println(email.getEmail()); // prints "test"
return Response.ok().build();
}
Finally you've to add com.sun.jersey.config.property.packages as <init-param> of your servlet adding the package or packages (separated by ,) pointing to your controller packages. For example in your web.xml:
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>org.app.controllers</param-value>
</init-param>

Jersey: Unable to Get Sub-Sub-Resource

I have a web application implementing REST API using Jersey. The web container id Tomcat
Here is a summary of the API:
/rest/patients
Gets a list of patients' metadata.
/rest/patients/{id}
Gets detailed data about a specific patient.
/rest/patients/{id}/visits
Gets a list of visits` metadata for a specific patient.
/rest/patients/{id}/visits/{visitId}
Gets detailed data about a specific visit of a specific patient.
My problem is that I can't get the sub-sub resources. For example, when I request /rest/patients/1 the detailed data of patient #1 is received correctly.
But when I request /rest/patients/1/visits I get 404 error, and the flow doesn't even enter the getVisits() method.
It looks like that when a request for a specific patient id received (patients/{id}), Jersey is directing it correctly from PatientsMetadataResource to PatientsResource.
But when a visits sub-sub-resource is being requested (patients/{id}/visits), Jersey doesn't direct it into the PatientsResource.
So how can I direct a sub resource along with all of its sub-sub resources into the same class?
Code for PatientsMetadataResource (The name is a bit vague, and I need to change it):
#Path("/patients")
public class PatientsMetadataResource {
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getPatients(#QueryParam("page") int pageIndex) {
//.... Loads, Builds and returns the patients' metadata list correctly
}
#GET
#Produces(MediaType.APPLICATION_JSON)
#Path("/{uid:\\d+}")
public PatientResource getPatient(#PathParam("uid") int uid) {
return new PatientResource(uid);
}
}
Code for PatientResource:
public class PatientResource {
private final int uid;
public PatientResource(int uid) {
this.uid = uid;
}
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getPatient() {
//Returns the patient correctly
System.out.println("A Patient was asked");
Patient patient = PersistentDataProvider.loadPatientByUid(uid);
return Response.ok(patient).build();
}
#GET
#Path("/visits")
#Produces(MediaType.APPLICATION_JSON)
public VisitsResource getVisits(#PathParam("uid") int patientUid) {
//The flow doesn't even enter here. A 404 is being returned instead.
System.out.println("Visits were asked");
return new VisitsResource(patientUid);
}
}
Code for Jersey part in web.xml:
<servlet>
<servlet-name>Jersey REST Service</servlet-name>
<servlet-class>
com.sun.jersey.spi.container.servlet.ServletContainer
</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>il.co.site_building.dvardy.resources</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey REST Service</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
Sub-Resource Locators aren't supposed to have HTTP method annotations
// #GET <--- Remove this
// #Produces(MediaType.APPLICATION_JSON)
#Path("/{uid:\\d+}")
public PatientResource getPatient(#PathParam("uid") int uid) {
return new PatientResource(uid);
}
Their main purpose is simply to forward request to the sub-resource class, not GET/POST/etc anything. When Jersey sees that HTTP method annotation, it no longer gets treated as a sub-resource locator.
Also you don't need need to pass the id. It will get passed accordingly
#Path("parent")
class ParentResource {
#Path("{id}")
public ChildResource getChild() {
return new ChildResource();
}
}
class ChildResource {
#GET
public Response get(#PathParam("id") long id) {}
#GET
#Path("something")
public Response something(#PathParam("id") long id) {}
}
Here GET 'parent/1' goes to ChildResource.get, passing the path param and GET parent/1/something goes to ChilsResource.something, passing the path param

JAX-RS REST service returning boolean as string

I am using JAX-RS to produce a RESTful service. However when requesting JSON, boolean values are returned as a quoted string {"boolValue":"true"} rather than a boolean value {"boolValue":true}.
A simple object
#XmlRootElement
public class JaxBoolTest {
private boolean working;
public boolean isWorking() {
return working;
}
public void setWorking(boolean working) {
this.working = working;
}
}
A simple JAX-RS REST service
#Path("/jaxBoolTest")
public class JaxBoolTestResouce {
#GET
#Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
public JaxBoolTest getJaxBoolTest() {
JaxBoolTest jbt = new JaxBoolTest();
jbt.setWorking(false);
return jbt;
}
}
And the result:
{"working":"false"}
How do I get the boolean values as boolean values rather than strings?
Using jaxson (http://jackson.codehaus.org/) to serialize this worked out of the box:
public class BooleanTest {
#Test
public void test() throws Exception{
System.out.println(new ObjectMapper().writeValueAsString(new JaxBoolTest()));
}
}
Produced this output:
{"working":false}
I highly recommend using Jackson for serializing JSON. It works great. I've used Jettison in the past, but had lots of issues with it. You will probably have to configure your jax-rs provider to use jackson, since it doesn't look like it's already using it.
Another tip: no need for #XmlRootElement when using jackson, unless you also want to provide jax-b xml using the same beans.
In my case the JSON output was displaying all properties as Strings (including primitive data types). Adding this to my web.xml fixed the problem.
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
#POST #Path("/users")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Object controlUserExisting(Object requestEntity) {
boolean result=RootBsn.controlUserExisting(requestEntity);
JSONObject json=new JSONObject();
json.put("result",result);
return json.toString();
}
retrieving user existing
Sended and returned{"result":true} (if exist)

Return a list of objects when using JAX-RS

How can I return a list of Question objects in XML or JSON?
#Path("all")
#GET
public List<Question> getAllQuestions() {
return questionDAO.getAllQuestions();
}
I get this exception:
SEVERE: Mapped exception to response: 500 (Internal Server Error)
javax.ws.rs.WebApplicationException:
com.sun.jersey.api.MessageException: A message body writer for Java
class java.util.Vector, and Java type
java.util.List, and MIME media
type application/octet-stream was not found
Try:
#Path("all")
#GET
public ArrayList<Question> getAllQuestions() {
return (ArrayList<Question>)questionDAO.getAllQuestions();
}
If your goal is to return a list of item you can use:
#Path("all")
#GET
public Question[] getAllQuestions() {
return questionDAO.getAllQuestions().toArray(new Question[]{});
}
Edit
Added original answer above
The same problem in my case was solved by adding the POJOMappingFeature init param to the REST servlet, so it looks like this:
<servlet>
<servlet-name>RestServlet</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
Now it even works with returning List on Weblogic 12c.
First of all, you should set proper #Produces annotation.
And second, you can use GenericEntity to serialize a list.
#GET
#Path("/questions")
#Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response read() {
final List<Question> list; // get some
final GenericEntity<List<Question>> entity
= new GenericEntity<List<Question>>(list) {};
return Response.ok(entity).build();
}
Your webservice may look like this:
#GET
#Path("all")
#Produces({ "application/xml", "application/*+xml", "text/xml" })
public Response getAllQuestions(){
List<Question> responseEntity = ...;
return Response.ok().entity(responseEntity).build();
}
then you should create a Provider, MessageBodyWriter:
#Produces({ "application/xml", "application/*+xml", "text/xml" })
#Provider
public class XMLWriter implements MessageBodyWriter<Source>{
}

Categories