So I recently moved from Jersey 1.x to 2.x and after a long list of problems finaly got it working. But whenever I try to reach a resource which returns a JSONObject I get problems. First of, here is my example method:
#GET
#Path("/foobar")
#Produces(MediaType.APPLICATION_JSON)
public JSONObject print2() throws JSONException {
JSONObject jsonObject = new JSONObject();
jsonObject.put("hi", 22);
return jsonObject;
}
Now if I use Jettison 1.3.8 for my JSONObject, I get the following if I try to reach this resource:
{"escapeForwardSlashAlways":true}
Not sure whats going on there. Then I tried some older versions of Jettison and also the org.json but these gives me this issue instead:
No serializer found for class org.json.JSONObject and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) )
Not sure why I get these problems when this kind of method worked fine for me on Jersey 1.x.
Assuming you are using Servlet 3.0 and above, the following example might help you to setup your environment to work with JSON data:
Dependency: if you are using Maven you need the following dependencies:
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>2.23.2</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.23.2</version>
</dependency>
If you are not using Maven, you need to add the correponding jars into your classpath.
Define POJOs to contain the data you want to serialize to JSON, for example,
public class User {
private String username;
private String email;
// getters + setters
}
Modify your resource method accordingly:
#GET
#Path("/foobar")
#Produces(MediaType.APPLICATION_JSON)
public User print2() {
User jsonObject = new User();
jsonObject.setUsername("Me");
jsonObject.setEmail("my#email.com");
return jsonObject;
}
Package and deploy, and the output should be:
{
"username": "Me",
"email": "my#email.com"
}
Note: This example is deployed and works on Tomcat 8.5.5.
I was struggling with the same issue, and eventually Jersey's bookmark exmple helped.
The problem is that your Jersey has no serializer for JSONObject and it tries to use BeanSerializer instead. Jettison JSONObject has only one public getter (isEscapeForwardSlashAlways) and org.json.JSONObject has no getters at all so BeanSerializer cannot be applied.
The solution is for (jettison json object):
Add dependency jersey-media-json-jettison:
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jettison</artifactId>
<version>2.26</version>
</dependency>
Register the jettison feature declaratively in your web.xml
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>org.glassfish.jersey.jettison.JettisonFeature</param-value>
<init-param>
Or programmatically in your application:
#ApplicationPath("/")
public class MyApplication extends ResourceConfig {
public MyApplication() {
registerClasses(UsersResource.class);
register(new JettisonFeature());
}
}
web.xml:
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>org.glassfish.jersey.examples.bookmark.MyApplication</param-value>
</init-param>
Perhaps org.json.JSONObject has such serializer feature for Jersey too, I don't know...
Another option is to allow the Response to convert your object to JSON. This gives the added benefit of adding the HTTP code as well. So you can return a 400, 404, 500 etc. and still send back a JSON response that can be acted upon by your JS. You should be able to drop your JSONObject in there since it's basically just extended Map - or any object for that matter.
#GET
#Path("/foobar")
#Produces(MediaType.APPLICATION_JSON)
public Response print2() {
User jsonObject = new User();
jsonObject.setUsername("Me");
jsonObject.setEmail("my#email.com");
return Response.status(Response.Status.OK).entity(jsonObject).build());
}
Related
I am working on a REST application built with Jackson-2.2.3.
Here is the Dependency:
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.2.3</version>
</dependency>
I have a simple endpoint to create a User as below:
#POST
#Path(value = "/addUser")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response createUser(User user) {
...
}
As this endpoint consumes JSON, when api users send JSON Jackson will desearilize to User object.
If user invoke the endpoint with faulty JSON like, missing a property or bad structure. I want to log the fault JSON as a string and ERROR.
How can I achieve that?
I tried using Filters but didn't work.
Consider handling the JsonParseException:
Exception type for parsing problems, used when non-well-formed content (content that does not conform to JSON syntax as per specification) is encountered.
To achieve it, you could use an ExceptionMapper:
#Slf4j
#Provider
public class JsonParseExceptionMapper implements ExceptionMapper<JsonParseException> {
#Override
public Response toResponse(JsonParseException exception) {
log.error("Cannot parse JSON", exception);
return Response.status(Response.Status.BAD_REQUEST)
.entity("Cannot parse JSON")
.type(MediaType.TEXT_PLAIN)
.build();
}
}
For logging purposes, you may also be interested in getting some details from JsonLocation using exception.getLocation().
To register the exception mapper in Jersey, refer to this answer.
I'd like to get JAXRS to push all json details for a specific class property into a JsonObject object.
Let's say I have the following class:
public class MyMessage implements Serializable {
private PayloadType payloadType;
private JsonObject payload;
}
REST method of:
#POST
#Path("/send")
#Consumes(MediaType.APPLICATION_JSON)
public Response send(MyMessage message)
I'd like to POST the following JSON, but have the payload property set as a javax.json.JsonObject object.
{
payloadType:'MESSAGE',
payload:{
subject:"My Subject",
body:"This is a message"
}
}
I'm running on Glassfish, so I was expecting that message reader for JsonObject were included with org.glassfish.jersey.media, which is support to be included in the GF4.1. Add the following maven dependency just causes ambiguous class exceptions.
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-processing</artifactId>
<version>2.22.1</version>
</dependency>
So there are a couple things stopping you here.
javax.json (or JSONP) does not handle POJOs. It only handles the javax.json API. If you what you are expecting is that the original provider handle the POJO, while the JSONP provider handle the javax.json, it doesn't work like that. Either you will use the one that handles the POJOs (which doesn't know javax.json or you use the one that handles javax.json. (We do make this happen below though :-)
Glassfish's default provider is MOXy. So we need to disable to to use any other provider. To disable MOXy, you need to set this property
ServerProperties.MOXY_JSON_FEATURE_DISABLE
to true. Either in your web.xml or your Java config.
So to make this work, we should make sure that we are using Jackson. Jackson has the jackson-datatype-jsr353 module which allows to do exactly what you are trying to do, javax.json in POJO.
Glassfish has the Jackson provider already, but you should add it anyway in a provided scope. So add these two dependencies
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.10.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jsr353</artifactId>
<version>2.3.3</version>
</dependency>
If yoo pay attention to the versions, I am using the same versions that are included in Glassfish 4.1. It uses Jersey 2.10.4 and Jackson 2.3.3. You want the versions to conflict. Even though the jersey-media-json-jackson is provided, it's still a good idea to try and use the same version as the server, when compiling.
Also you should register the JacksonFeature with your application.
And the last thing we need is to register the JSR353Module so that we can get the javax.json support with Jackson. To do that just register the following provider with your application.
#Provider
public class ObjectMapperProvider implements ContextResolver<ObjectMapper> {
private final ObjectMapper mapper;
public ObjectMapperProvider() {
mapper = new ObjectMapper();
mapper.registerModule(new JSR353Module());
}
#Override
public ObjectMapper getContext(Class<?> type) {
return mapper;
}
}
That's it. I've tested it, and it should work.
message: Could not find MessageBodyWriter for response object of type:
java.util.ArrayList of media type: application/json
Description: The server encountered an internal error (Could not find
MessageBodyWriter for response object of type: java.util.ArrayList of
media type: application/json) that prevented it from fulfilling this
request
#GET
#Path("/{userName}/questions")
//#Produces("application/json")
public Response getUserQuestions(#PathParam("userName") String userName){
UserDAO userDAO = new UserDAO();
List<Question> questions = userDAO.getUserQuestionsByUserName(userName);
GenericEntity<List<Question>> entity = new GenericEntity<List<Question>>(questions){};
return Response.status(200).entity(entity).type(MediaType.APPLICATION_JSON).build();
}
I have got the resteasy jackson provider in the classpath.
Tried changing the return type form ArrayList to List, then wrapping it in GenericEntity based on resteasy response, but still getting the same issue.
Running on tomcat7.
Thanks.
I solved this exception by adding resteasy-jackson-provider.jar to classpath
Refer https://bitbucket.org/arcbees/gaestudio/issue/2/need-resteasy-jackson-provider-on
finally solved it using the Gson library instead of relying on json.
did not wrap in Generic Entity either. Here is the code that works
#GET
#Path("/{userName}/questions")
public Response getUserQuestions(#PathParam("userName") String userName){
UserDAO userDAO = new UserDAO();
List<Question> questions = userDAO.getQuestionsByUserName(userName);
Gson gson = new GsonBuilder().setExclusionStrategies(new UserQuestionsExclStrat()).create(); //.serializeNulls()
String json = gson.toJson(questions);
System.out.println(json);
return Response.status(200).entity(json).build();
}
Had to use the exclusion strategy to avoid cyclic reference. here is the link for that:stackoverflow error during json conversion (hibernate bi-directional mapping)
Faced same issue resolved by adding #XMLRootElement in class used in ArrayList
By adding this dependency I was able to solve this issue.
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.10.1</version>
</dependency>
I am trying to save data on my database with a web service POST wich serializes a HTML form to save a object. The rest client firefox says this:
"The server refused this request because the requested entity is in a format not supported by the requested resource for the requested method"
The eclipse console shows the message:
org.jasig.cas.client.util.CommonUtils - safeGetParameter called on a POST HttpServletRequest for LogoutRequest. Cannot complete check safely. Reverting to standard behavior for this Parameter
I understand that the object that i want to save is not valid, but I don't see what the problem is.
#RequestMapping(value="/solicitudCita", method = RequestMethod.POST)
public #ResponseBody String putSolicitud(#ModelAttribute("Solicitud") Solicitud solicitud) throws Exception{
System.out.println(solicitud.toString());
solicitudCitaAppMService.createOrUpdate(solicitud);
String solicitudAdded = "Solicitud de cita -> {" + solicitud.toString() + "} aƱadida";
System.out.println(solicitud);
return solicitudAdded;
}
Help me please
Thanks
If you want to call this controller in a RESTful manner, you have to annotate the solicitud parameter as #RequestBody. Second, you have to have the Jackson libraries in you classpath so Spring can pick them up and use them for unmarshalling the object.
If you use Maven, use these dependencies:
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.12</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.12</version>
</dependency>
BTW, why do you have to serialize the HTML form and sent it across? I would suggest you use a REST client, for instance this one, that is available in the Chrome WebStore.
I'm trying to set up a toy application (which may turn in to a real application someday). I'm running into a problem with Wink and Jackson. I've got two applications: one runs wink-server on jetty and seems to be providing some JSON data just fine; one runs wink-client on jetty and receives the JSON data just fine. The problem lies in automagically deserializing the JSON data back into my Java bean.
Here's the code I use in my wink client action:
RestClient client = new RestClient();
Resource resource = client.resource("http://localhost:8081/helloworld");
User user = resource.accept(MediaType.APPLICATION_JSON).get(User.class);
Here's the error I receive when I try to run the Struts action:
java.lang.RuntimeException: No javax.ws.rs.ext.MessageBodyReader found for type class my.package.structure.User and media type application/json. Verify that all entity providers are correctly registered.
org.apache.wink.client.internal.handlers.ClientResponseImpl.readEntity(ClientResponseImpl.java:123)
org.apache.wink.client.internal.handlers.ClientResponseImpl.getEntity(ClientResponseImpl.java:65)
org.apache.wink.client.internal.handlers.ClientResponseImpl.getEntity(ClientResponseImpl.java:52)
org.apache.wink.client.internal.ResourceImpl.invoke(ResourceImpl.java:186)
org.apache.wink.client.internal.ResourceImpl.get(ResourceImpl.java:294)
my.package.structure.action.HelloWorldAction.execute(HelloWorldAction.java:29)
...
If I replace the last line in the first code snippet with the following line, everything works fine and dandy.
String message = resource.accept(MediaType.APPLICATION_JSON).get(String.class);
ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(message, User.class);
It's clear that the data is getting across just fine, but the problem seems to lie with the fact that the JacksonJsonProvider class is not registered with Wink client. I've seen a lot of ways to register the provider with the Wink server, but not the Wink client.
Is it possible to do make the first code snippet operate properly? If so, how?
(As an aside, the other problem may be that I'm missing annotations on my User class. Right now there aren't any. Maybe I need some...)
Step 1: Create a class that extends javax.ws.rs.core.Application that allows you to set singletons.
import java.util.Collections;
import java.util.Set;
import javax.ws.rs.core.Application;
public class ClientApplication extends Application {
private Set<Object> singletons = Collections.emptySet();
#Override
public Set<Object> getSingletons() {
return singletons;
}
public void setSingletons(final Set<Object> singletons) {
this.singletons = singletons;
}
}
Step 2: In your action, create a org.apache.wink.client.ClientConfig for your org.apache.wink.client.RestClient. This allows you add the org.codehaus.jackson.jaxrs.JacksonJsonProvider to your providers list.
ClientApplication clientApplication = new ClientApplication();
Set<Object> s = new HashSet<Object>();
s.add(new JacksonJsonProvider());
clientApplication.setSingletons(s);
ClientConfig clientConfig = new ClientConfig().applications(clientApplication);
RestClient restClient = new RestClient(clientConfig);
Step 3: Create the org.apache.wink.client.Resource, use the get(Class<T> responseEntity) method and everything will now work as expected.
Resource resource = client.resource("http://localhost:8081/helloworld");
User user = resource.accept(MediaType.APPLICATION_JSON).get(User.class);
If you want to be really slick about it, you can use Spring to set up a ClientConfig bean and inject it in to your actions. Then, you can just call new RestClient(clientConfig) every time and not worry about replicating the entire setup.
i ran into this issue when trying to write some integration tests that POST an object for my rest plugin.
Rather then spinning out a new class you can provide the Jackson provider with an inline class.
#Before
public void setup(){
javax.ws.rs.core.Application app = new javax.ws.rs.core.Application() {
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<Class<?>>();
classes.add(JacksonJaxbJsonProvider.class);
return classes;
}
};
//create auth handler
clientConfig = new ClientConfig();
clientConfig.applications(app);
BasicAuthSecurityHandler basicAuthSecurityHandler = new BasicAuthSecurityHandler();
basicAuthSecurityHandler.setUserName(USERNAME);
basicAuthSecurityHandler.setPassword(PASSWORD);
clientConfig.handlers(basicAuthSecurityHandler);
//create client usin auth and provider
client = new RestClient(clientConfig);
}
Then you can post and consume your annotated objects.
#Test
public void aReadWriteTokenCanBeCreatedAsRequested(){
ClientResponse response = client.resource(resourceUrlToken).contentType(MediaType.APPLICATION_JSON).accept(MediaType.APPLICATION_JSON).post(readAndWriteToken);
assertEquals("Could not create token needed for test",200,response.getStatusCode());
readAndWriteToken = response.getEntity(TokenResource.class);
assertNotNull("Returned token does not have UUID",readAndWriteToken.getUuid());
}
If you're using maven you can make sure Jackson is on the test classpath (check for compatible versions):
<!-- TEST DEPENDENCIES -->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-jaxrs</artifactId>
<version>1.9.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-xc</artifactId>
<version>1.9.1</version>
<scope>test</scope>
</dependency>
I wish I could help with registration; but with respect to annotations, I don't think you should need any for Jackson to try to deserialize value. If you are missing something you need you would get different kind of exception.