I have a controller class that looks like:
#RequestMapping(value="/enter/two-factor/blacklist", method = RequestMethod.GET, produces = "application/json")
public #ResponseBody String getBlacklist(int days) {
List<Honey> honey = honeyService.fetchAllPings(days);
List<Locate> locations = honeyService.parseDistinctLocations(honey);
return GeneralUtil.convertToJson(locations);
}
The 'GeneralUtil.convertToJson()' method returns a pretty-print string with this code:
public static String convertToJson(List<Locate> locations){
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String json = "";
try {
json = gson.toJson(new Blacklist(locations));
} catch (Exception e){
e.printStackTrace();
}
JsonParser jp = new JsonParser();
JsonElement je = jp.parse(json);
String prettyJsonString = gson.toJson(je);
System.out.println(prettyJsonString);
return prettyJsonString;
}
However, when the page renders, the JSON is not pretty-printed. What am I missing?
You're overlooking the fact that #ResponseBody with a String return type will cause a StringHttpMessageConverter to write the value returned to the HTTP response body. This HttpMessageConverter will produce a content-type of text/plain. I believe browsers don't render new line characters or trim whitespace between them. Your pretty printing gets lost.
What you should do is register your own GsonHttpMessageConverter or MappingJackson2HttpMessageConverter which is set up to do pretty printing. Then change your handler method's return type to List<Locate> and return locations directly. These HttpMessageConverter implementations produce application/json which browsers should render literally.
(Gson and ObjectMapper instances are thread safe. There's absolutely no reason to reinstantiate those classes for every request.)
Related
A pleasant day.
I am having trouble with simply displaying string in raw JSON format using Postman.
This is what I have in my Java code:
#RestController
public class HeroController {
#RequestMapping(method = {RequestMethod.POST}, value = "/displayHero")
#ResponseBody
public Map<String, String> displayInfo(String name){
//System.out.println(name);
Map<String, String> imap = new LinkedHashMap<String, String>();
map.put("hero", name);
return imap;
}
}
Every time I test this in Postman, I always get null (again if I am using raw format):
{
"hero": null
}
But using form-data, on the other hand, displays just what I entered.
{
"hero": "wolverine"
}
Any information, or should do in Postman to make this raw format works instead of form-data? By the way, the raw format value is JSON(application/json), and in the Header Tab, the value of Content-Type is application/json; charset=UTF-8.
Thank you and have a nice day ahead.
Try the following code for consuming the request body as JSON, in spring boot:-
#RequestMapping(value = "/displayHero", method = POST, consumes = APPLICATION_JSON_VALUE, produces = APPLICATION_JSON_VALUE)
#ResponseBody
public String displayInfo(HttpEntity<String> httpEntity) {
String json = httpEntity.getBody();
// json contains the plain json string
// now you can process the json object information as per your need
// and return output as per requirements.
return json;
}
This code will accept json body of POST Request and then return it as response.
i currently use Google's GSON library to serialize/deserialize rest service responses.
But i have a little problem. My response object has T response attribute.
public class IninalResponse<T> {
private int httpCode;
private String description;
private T response;
private HashMap<String,String> validationErrors;
...
}
I would like to get response attribute according to object type which i specified. At this example i specified with GetAccessTokenResponse to deserialize T response attribute in the piece of code below.
public IninalResponse getAccessToken(String apikey) {
String path = "https://sandbox-api.ininal.com/v2/oauth/accesstoken";
return doPostIninal(apikey,path,null,GetAccessTokenResponse.class);
}
GSON library successfully deserializes IninalResponse object except for T response field. Gson deserializes it as LinkedTreeMap typed object.
public <T,V> IninalResponse doPostIninal(String apikey, String path,V requestBody, T response) {
RestTemplate template = restClient.getRestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.AUTHORIZATION,apikey);
headers.add(HttpHeaders.DATE, "");
headers.add(HttpHeaders.CONTENT_TYPE, "application/json");
HttpEntity<?> request = new HttpEntity<Object>(requestBody,headers);
ResponseEntity<String> accessTokenResponse = restClient.getRestTemplate().postForEntity(path,request,String.class);
IninalResponse<T> responseBody = new IninalResponse<T>();
responseBody = new Gson().fromJson(accessTokenResponse.getBody(),responseBody.getClass());
System.out.println(accessTokenResponse);
return responseBody;
}
Still i have no idea why gson could not deserialize ? What exactly am i missing ?
Due to Java's type erasure, you need to make a trick to serialize/deserialize generic types:
Type fooType = new TypeToken<IninalResponse<TheType>>() {}.getType();
gson.fromJson(accessTokenResponse.getBody(), fooType);
where TheType is the type you passed in during serialization (String I suppose).
Serialization goes as this:
Type fooType = new TypeToken<IninalResponse<String>>() {}.getType(); // I assume it was a String here.
gson.toJson(someString, fooType);
How can I get the raw json string from spring rest template? I have tried following code but it returns me json without quotes which causes other issues, how can i get the json as is.
ResponseEntity<Object> response = restTemplate.getForEntity(url, Object.class);
String json = response.getBody().toString();
You don't even need ResponseEntitys! Just use getForObject with a String.class like:
final RestTemplate restTemplate = new RestTemplate();
final String response = restTemplate.getForObject("https://httpbin.org/ip", String.class);
System.out.println(response);
It will print something like:
{
"origin": "1.2.3.4"
}
I would like to set the produces = text/plain to produces = application/json when I encounter an error.
#RequestMapping(value = "/v0.1/content/body", method = RequestMethod.GET, produces = "text/plain")
#ResponseBody
public Object getBody(#RequestParam(value = "pageid") final List<String> pageid, #RequestParam(value = "test") final String test) {
if (!UUIDUtil.isValid(pageid)) {
Map map = new HashMap();
map.put("reason", "bad pageId");
map.put("pageId", pageId);
map.put("test", test);
return new ResponseEntity<Object>(map, HttpStatus.BAD_REQUEST);
}
return "hello";
}
The problem with this code is that it doesn't print the error as json when I send an invalid pageId. It gives me a HTTP 406 error Not acceptable, because it expects to produce text/plain but I didn't return a String.
The cleanest way to handle errors is to use #ExceptionHandler:
#ExceptionHandler(EntityNotFoundException.class) //Made up that exception
#ResponseBody
#ResponseStatus(value = HttpStatus.NOT_FOUND)
public ErrorObject handleException(Exception e) {
return new ErrorObject(e.getMessage());
}
Then assuming you've configured your resolvers properly and put the right JSON serialization library in the classpath, the instance of ErrorObject will be returned to the client as a JSON response.
Of course you can set up multiple #ExceptionHandler methods as needed.
Now I'm learning how to use Gson library to set and get data from webservice in Json format, but its best practices and strategies are a bit dark for me so I will be very delightful if somebody would explain more about it.
I've created an Entity class to get response entity from server:
public class Response
{
#SerializedName("Type")
public String Type;
#SerializedName("result")
public String result;
}
and in AsyncTask class I've used:
Response _Response = new Response();
try
{
String _url = Global.Url_Request ;
Map<String, String> Params = new HashMap<String, String>();
Params.put("PhoneNumber", this.User_PhoneNumber);
String json = new GsonBuilder().create().toJson(Params, Map.class);
HttpClient httpclient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(_url);
httpPost.setEntity(new StringEntity(json));
httpPost.setHeader("Accept", "application/json");
httpPost.setHeader("Content-type", "application/json");
HttpResponse getResponse = httpclient.execute(httpPost);
HttpEntity returnEntity = getResponse.getEntity();
is = returnEntity.getContent();
Gson gson = new Gson();
Reader reader = new InputStreamReader(is);
_Response = gson.fromJson(reader, Response.class);
}
catch (Exception e)
{
_Response.Type= "Error";
_Response.result= "Data Is Wrong";
}
return _Response;
It works fine with creating an Entity Object for every different http POST call, but my questions are:
What is the best practice for handling webservices with different response objects?
How can I handle this situation: if data sent ok then return specific Jsonarray; if not, return a Response object to detect something is wrong. Should I use Custom typeAdapter?(sample code would be great)
If webservice returns an empty response gson.fromJson would throw an **IllegalStateException: Expected a string but was BEGIN_OBJECT** how can i prevent this?
Thanks in advance
1. What is the best practice for handling webservices with different response objects?
I think that this depends on the kind of control you have. If you code also the webservice, you could create a big container object that has may fields. Each of these fields is one of the possible responses you can pass between client and server. If you have not control on what the server can reply, and it can differ a lot, JsonParser is your best friend. You can use it to snoop inside JSON and decide the right strategy to handle the response.
Let's do an example for case one. I declare these classes:
public static class GenericResponse{
public ServerException exception;
public StandardResponse1 responseType1;
public StandardResponse2 responseType2;
#Override
public String toString() {
return "GenericResponse [exception=" + exception + ", responseType1=" + responseType1 + ", responseType2=" + responseType2 + "]";
}
}
public static class ServerException{
public int error;
public String message;
}
public static class StandardResponse1{
public List<Integer> list;
public Date now;
}
With this kind of classes, I can parse:
{"responseType1":{"list":[1,2],"now":"Nov 25, 2013 9:26:51 PM"}}
or
{"exception":{"error":-1,"message":"Don\u0027t do this at home"}}
For example, if I get from server the second type of response, this code:
GenericResponse out = g.fromJson(fromServerStream, GenericResponse.class);
System.out.println(out);
will return me:
GenericResponse [exception=stackoverflow.questions.Q20187804$ServerException#1e9d085, responseType1=null, responseType2=null]
All you have to do is to check your fields to see what actually the server replied.
Case two. You cannot control the JSON, so the server can reply
[13,17]
or
{"error":-1,"message":"Don\u0027t do this at home"}
In this case you cannot pass directly the class type to Gson as before, but you have to check things. I would solve this problem with a JsonParser.
Gson g = new Gson();
JsonParser jp = new JsonParser();
JsonElement o = jp.parse(s);
if (o.isJsonArray()){
List<Integer> list = (List) g.fromJson(o, listType1);
System.out.print(list);
}
else{
ServerException e = g.fromJson(s, ServerException.class);
System.out.print(e);
}
Using JsonObject/JsonArray and so on, is what happens inside a TypeAdapter. In the adapter you start with the JsonElement that is already parsed. There are many good example of it on SO, this for example.
How can I handle this situation: if data sent ok then return specific Jsonarray; if not, return a Response object to detect something is wrong. Should I use Custom typeAdapter?(sample code would be great)
Do you mean you want to parse this kind of response? Examples of point 1 show this.
If webservice returns an empty response gson.fromJson would throw an **IllegalStateException: Expected a string but was BEGIN_OBJECT how can i prevent this?**
JsonParser/TypeAdapter is again the solution. You can check if JsonElement is null or if is a primitive type (String, Integer, Boolean) and deal it.