JAX-RS response producing escaped JSON for a string - json

I am using JAX-RS to produce a webservice. For this piece of the web service, I have a JSON string that I need to send to the user, but the problem is that JAX-RS is escaping the String before sending it. Here is the concrete problem.
The service looks like this:
#GET
#Produces("application/json")
public String serializeConfiguration() {
return exportConfiguration();
}
User goes to http://mycompany.com/export-configuration
User wants a response of:
{
"myconfig" : "some stuff"
}
but instead gets:
"{\n \"myconfig\" : \"some stuff\"\n }"
What is going on here is obviously that the string is being escaped. Instead, I want the raw string, but keeping the same content type.

If you already have JSON as String it should work if you create a Response with this entity:
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response config() {
return Response.ok(exportConfiguration()).build();
}

To answer my own question, looks like I need to write to the response object directly. Like this:
#GET
public void serializeConfiguration(#Context HttpServletResponse response) throws IOException {
response.setContentType(MediaType.APPLICATION_JSON);
response.setStatus(200);
response.setCharacterEncoding(Charsets.UTF_8.name());
response.getWriter().write(exportConfiguration());
response.getWriter().close();
}

Related

JSON RAW REQUEST in controller

I have the code as below
#RequestMapping(method = RequestMethod.POST)
public #ResponseBody ServiceResponse submitCustomerOrder(#RequestBody SubmitCustomerOrderRequest submitCustomerOrderRequest,HttpServletRequest request)
{
String json=????
}
I need the de-serialization should happen to SubmitCustomerOrderRequest object but also need the raw json request in the string for logging the request purpose.
Can anybody help me on this.
Use google's Gson library
log.info(new Gson().toJson(submitCustomerOrderRequest))

POSTing `JSON` to an `ArrayList` gets the response "Request JSON Mapping Error"

I have a problem POSTing JSON to an ArrayList
I have a class Plan
public class Plan {
private String planId;
private String planName;
:
:
}
and an ArrayList of Plan - PlanList
public class PlanList {
private List<Plan> plans = new ArrayList<Plan>();
:
:
}
I have POST and GET REST APIs
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Plan createPlan(#CookieParam(SmsHttpHeaders.X_SMS_AUTH_TOKEN) String token, Plan plan, #HeaderParam("Organization-Id") String organizationIdByService);
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public PlanList createPlans(#CookieParam(SmsHttpHeaders.X_SMS_AUTH_TOKEN) String token, PlanList plans, #HeaderParam("Organization-Id") String organizationIdByService);
#GET
#Produces(MediaType.APPLICATION_JSON)
public PlanList retrieveAllPlans(#CookieParam(SmsHttpHeaders.X_SMS_AUTH_TOKEN) String token, #HeaderParam("Organization-Id") String organizationIdByService);
When I GET retrieveAllPlans, I get back the following JSON, just as I expect.
{
"plans": [
{
"planId":"1",
"planName":"Plan 1"
},
{
"planId":"2",
"planName":"Plan 2"
},
{
"planId":"3",
"planName":"Plan 3"
}
]
}
POSTing a single Plan, createPlan, works correctly.
However, when I try to POST to createPlans in the same format that the GET returns, I get the response "Request JSON Mapping Error".
Is the JSON incorrectly formatted? Is my REST definition wrong?
Both of your post functions are being mapped to the same http endpoint. There is probably a #Path notation on the class specifying a single endpoint for all its methods and RestEasy is trying to distinguish by http method (post, get, etc..).
You'll need to specify a unique #Path annotation for each post function. Say:
#Path("/plan")
For the first, and
#Path("/plans")
For the second.
The problem is, that when you try to POST to createPlans, the request is being handled by createPlan method, because both methods are handling the same URL.
The soultion is to make two different #Path for these methods.

Is it possible to get jersey to read json variables our of a request body without using a bean?

In jersey a Java bean can be auto-deserialized from within a request body but what if I want to read a parameter without creating a special type. Is it possible to do this using annotations.
My current code is:
public class RequestData {
String param;
}
...
public Response readData(RequestData data) {
data.getParam();
...
}
I want it to be something like:
public Response readData(#RequestParam("param") String param) {
...
}
If its not already clear the input JSON is:
{
"param":"some value"
}
The type of your input JSON is Map<String, String> so if you want to have undifferentiated input you could use that as your request parameter and read the values that you require.
Note that #RequestParam looks at the request parameters and not the body, so it's a different beast.
You do this by letting Jersey pass you String as is (as per annotations), and then data-bind it using Jackson ObjectMapper (thing Jersey uses internally for JSON binding):
Map<String,Object> map = objectMapper.readValue(param, Map.class);
to get access to ObjectMapper, you can use JAX-RS injection annotation (#Context I think?) in the resource class:
#Context
private ObjectMapper objectMapper;

Exception with REST implementation with jersey

I am building a REST service with jersey and I am stuck with a weird exception.
I want to hit a REST uri similar to:
http://localhost:9889/rest/Users/{userid}
the content to be sent with the request is in JSON similar to:
{
"attr1":"name",
"attr2":"age"
}
The endpoint url code is as shwon below:
#Path("/rest/Users")
class Users
{
#GET
#Produces(MediaType.TEXT_PLAIN)
#Path("/{userId}")
#Consumes(MediaType.APPLICATION_JSON)
public String getUserInfoQuery(
QueryDoc reqJSON,
#PathParam("userId") String userId,
#HeaderParam("Session-Token") String sessionId,
#HeaderParam("Authorization") String authToken)
)
{
.
.
.
.
}
}
And QueryDoc resource looks like this:
#XmlRootElement
#XmlAccessorType(XmlAccessType.NONE)
public class QueryDoc
{
#XmlElement(name = "attr1")
private String attr1;
#XmlElement(name = "attr2")
private String attr2;
//getters and setters
.
.
.
.
}
When I am starting the server, an exception is thrown
com.sun.jersey.api.container.ContainerException: Fatal issues found at
class com.test.Users. See logs for more details.
at com.sun.jersey.server.impl.application.WebApplicationImpl.newResourceClass(WebApplicationIm....
I could find this exception here http://www.skybert.net/java/jersey/
and as mentioned in this link..the reason is
public String getUserInfoQuery(
QueryDoc reqJSON,
reqJSON is not being annotated. If I annotate it with some annotation the exception is not thrown when server is started but in this case url response is meaningless. If i remove this parameter the url works but it doesn't consume the request JSON.
How can I make it work where I want to consume JSON content of the request as well as HeaderParams and PathParams
Is your getUserInfoQuery() method annotated with #GET annotation? If so, it is mapped to HTTP GET request. You cannot send entity in HTTP GET, so the unannotated parameter does not make sense (as Jersey maps entity to the unannotated param, but as said, in case of GET there is no entity).
Change your method getUserInfoQuery() to #PUT. In the QueryDoc class remove all annotations except #XmlRootElement. Since the attribute name you pass in the request body is same as the those in QueryDoc #XmlElement is not required. Moreover #XmlElement should be given to ge getter method.This is a good article on ReST with Jersey.
Try this:
I had the same exception with no additional details on Jersey's 'newResourceClass' method;
after hours of debugging, I realized it happened due to ambiguous URIs.
Check your URIs and eliminate any possible duplicates, such as this one:
#Path("/users")
#GET
#Produces(MediaType.APPLICATION_JSON)
public List<String> getUsers() {
...
}
#Path("/users") // BAD
#GET
#Produces(MediaType.APPLICATION_JSON)
public String getUserById(#QueryParam("userId") String userId) {
...
}

Consuming JSON object in Jersey service

I've been Googling my butt off trying to find out how to do this: I have a Jersey REST service. The request that invokes the REST service contains a JSON object. My question is, from the Jersey POST method implementation, how can I get access to the JSON that is in the body of the HTTP request?
Any tips, tricks, pointers to sample code would be greatly appreciated.
Thanks...
--Steve
As already suggested, changing the #Consumes Content-Type to text/plain will work, but it doesn't seem right from an REST API point of view.
Imagine your customer having to POST JSON to your API but needing to specify the Content-Type header as text/plain. It's not clean in my opinion. In simple terms, if your API accepts JSON then the request header should specify Content-Type: application/json.
In order to accept JSON but serialize it into a String object rather than a POJO you can implement a custom MessageBodyReader. Doing it this way is just as easy, and you won't have to compromise on your API spec.
It's worth reading the docs for MessageBodyReader so you know exactly how it works. This is how I did it:
Step 1. Implement a custom MessageBodyReader
#Provider
#Consumes("application/json")
public class CustomJsonReader<T> implements MessageBodyReader<T> {
#Override
public boolean isReadable(Class<?> type, Type genericType,
Annotation[] annotations,MediaType mediaType) {
return true;
}
#Override
public T readFrom(Class<T> type, Type genericType, Annotation[] annotations,
MediaType mediaType, MultivaluedMap<String, String> httpHeaders,
InputStream entityStream) throws IOException, WebApplicationException {
/* Copy the input stream to String. Do this however you like.
* Here I use Commons IOUtils.
*/
StringWriter writer = new StringWriter();
IOUtils.copy(entityStream, writer, "UTF-8");
String json = writer.toString();
/* if the input stream is expected to be deserialized into a String,
* then just cast it
*/
if (String.class == genericType)
return type.cast(json);
/* Otherwise, deserialize the JSON into a POJO type.
* You can use whatever JSON library you want, here's
* a simply example using GSON.
*/
return new Gson().fromJson(json, genericType);
}
}
The basic concept above is to check if the input stream is expected to be converted to a String (specified by Type genericType). If so, then simply cast the JSON into the specified type (which will be a String). If the expected type is some sort of POJO, then use a JSON library (e.g. Jackson or GSON) to deserialize it to a POJO.
Step 2. Bind your MessageBodyReader
This depends on what framework you're using. I find that Guice and Jersey work well together. Here's how I bind my MessageBodyReader in Guice:
In my JerseyServletModule I bind the reader like so --
bind(CustomJsonReader.class).in(Scopes.SINGLETON);
The above CustomJsonReader will deserialize JSON payloads into POJOs as well as, if you simply want the raw JSON, String objects.
The benefit of doing it this way is that it will accept Content-Type: application/json. In other words, your request handler can be set to consume JSON, which seems proper:
#POST
#Path("/stuff")
#Consumes("application/json")
public void doStuff(String json) {
/* do stuff with the json string */
return;
}
Jersey supports low-level access to the parsed JSONObject using the Jettison types JSONObject and JSONArray.
<dependency>
<groupId>org.codehaus.jettison</groupId>
<artifactId>jettison</artifactId>
<version>1.3.8</version>
</dependency>
For example:
{
"A": "a value",
"B": "another value"
}
#POST
#Path("/")
#Consumes(MediaType.APPLICATION_JSON)
public void doStuff(JSONObject json) {
/* extract data values using DOM-like API */
String a = json.optString("A");
Strong b = json.optString("B");
return;
}
See the Jersey documentation for more examples.
I'm not sure how you would get at the JSON string itself, but you can certainly get at the data it contains as follows:
Define a JAXB annotated Java class (C) that has the same structure as the JSON object that is being passed on the request.
e.g. for a JSON message:
{
"A": "a value",
"B": "another value"
}
Use something like:
#XmlAccessorType(XmlAccessType.FIELD)
public class C
{
public String A;
public String B;
}
Then, you can define a method in your resource class with a parameter of type C. When Jersey invokes your method, the JAXB object will be created based on the POSTed JSON object.
#Path("/resource")
public class MyResource
{
#POST
public put(C c)
{
doSomething(c.A);
doSomethingElse(c.B);
}
}
This gives you access to the raw post.
#POST
#Path("/")
#Consumes("text/plain")
#Produces(MediaType.APPLICATION_JSON)
public String processRequset(String pData) {
// do some stuff,
return someJson;
}
Submit/POST the form/HTTP.POST with a parameter with the JSON as the value.
#QueryParam jsonString
public desolveJson(jsonString)
Some of the answers say a service function must use consumes=text/plain but my Jersey version is fine with application/json type. Jackson and Jersey version is
jackson-core=2.6.1, jersey-common=2.21.0.
#POST
#Path("/{name}/update/{code}")
#Consumes({ "application/json;charset=UTF-8" })
#Produces({ "application/json;charset=UTF-8" })
public Response doUpdate(#Context HttpServletRequest req, #PathParam("name") String name,
#PathParam("code") String code, String reqBody) {
System.out.println(reqBody);
StreamingOutput stream = new StreamingOutput() {
#Override public void write(OutputStream os) throws IOException, WebApplicationException {
..my fanzy custom json stream writer..
}
};
CacheControl cc = new CacheControl();
cc.setNoCache(true);
return Response.ok().type("application/json;charset=UTF-8")
.cacheControl(cc).entity(stream).build();
}
Client submits application/json request with a json request body. Servlet code may parse string to JSON object or save as-is to a database.
SIMPLE SOLUTION:
If you just have a simple JSON object coming to the server and you DON'T want to create a new POJO (java class) then just do this.
The JSON I am sending to the server
{
"studentId" : 1
}
The server code:
//just to show you the full name of JsonObject class
import javax.json.JsonObject;
#Path("/")
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Response deleteStudent(JsonObject json) {
//Get studentId from body <-------- The relevant part
int studentId = json.getInt("studentId");
//Return something if necessery
return Response.ok().build();
}