I am working on a project in which I am making a call to one of my servers using RestTemplate which is running a restful service and getting the response back from them.
The response that I will be getting from my server can be either of these error responses (that's all I have for error response) if something has gone wrong -
{"warning": "user_id not found", "user_id": some_user_id}
{"error": "user_id for wrong partition", "user_id": some_user_id, "partition": some_partition}
{"error": "missing client id", "client_id":2000}
or below successful response (it can be any random json string key can also be different) -
{"#data": {"oo":"1205000384","p":"2047935"}
If I am getting any error response as mentioned above, then I am deserializing it (my bad :( ) so that I can log them as an error with a specific error or warning I got front the server which can be for example - user_id not found or missing client id.
If it is a successful response then also I am deserializing it which I don't need for my use case as we don't have any POJO and I just need to return the response as it is which I have got from the server.
In my use case, I don't need to deserialize my response string if it is a successful response as we don't have any POJO for that and we are returning the response string as it is which we have got from the server. But just for logging specific error messages (if I am getting error response from the server) I am deserializing it which I am thinking is unnecessary. There might be better solution for my use case.
Below is my Java client which is calling Callable task using future.get -
public class TestingClient implements IClient {
private ExecutorService service = Executors.newFixedThreadPool(10);
private RestTemplate restTemplate = new RestTemplate();
#Override
public String executeSync(ClientKey keys) {
String response = null;
try {
ClientTask ClientTask = new ClientTask(keys, restTemplate);
Future<String> future = service.submit(ClientTask);
response = handle.get(keys.getTimeout(), TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
} catch (Exception e) {
}
return response;
}
}
And now below is my ClientTask class which implements Callable interface. In the call method, I am generating an URL and then hit the server using RestTemplate and get the response back -
class ClientTask implements Callable<String> {
private ClientKey cKeys;
private RestTemplate restTemplate;
public ClientTask(ClientKey cKeys, RestTemplate restTemplate) {
this.restTemplate = restTemplate;
this.cKeys = cKeys;
}
#Override
public String call() throws Exception {
// .. some code here
String url = "some_url";
String response = restTemplate.getForObject(url, String.class);
String test = checkJSONResponse(response);
return test;
}
private String checkJSONResponse(final String response) throws Exception {
// may be there are some better way of doing it for my scenario instead of using GSON
Gson gson = new Gson();
String str = null;
JsonObject jsonObject = gson.fromJson(response, JsonObject.class); // parse it, may be performance issues here/
if (jsonObject.has("error") || jsonObject.has("warning")) {
final String error = jsonObject.get("error") != null ? jsonObject.get("error").getAsString() : jsonObject
.get("warning").getAsString();
// log specific `error` here using log4j
str = response;
} else {
str = response;
}
return str;
}
}
As you can see in my above code we are deserializing the JSON string only to log specific error messages if we are getting any error response back. But for successful response we don't need any deserialization but still we are doing it.
Is there any better way of solving this problem? Because currently I am seeing some performance issues with the GSON deserialization.
The only way I can identify successful response along with error response is with error or warning in the response so I am thinking of using regular expressions which can identify error or warning as the key in the response string. If they contain error or warning in the response string then extract the specific error or warning message and log it. But not sure whether this will have any performance benefit or not.
Is there any other better way of solving this problem without using GSON deserialization.
It is a good practice to use HTTP status codes for your responses (e.g. BAD_REQUEST, NOT_FOUND). Return one of them from the server and then check on the client. It will allow to parse response only if some error code is returned:
String result = restTemplate.execute("url", HttpMethod.GET, null, new HttpMessageConverterExtractor<String> {
#Override
public MyEntity extractData(ClientHttpResponse response)
throws IOException {
String result = super.extractData(response);
if (response.getStatusCode() != HttpStatus.OK) {
// parse message and log only for some error code
JsonObject errorJson = parse(result);
log.warn("Got {} status error, with message [{}]", response.getStatusCode(), errorJson.get("warning"));
}
return result;
}
});
You do not need to deserialize to a POJO.
A simple JSON parser such as the one found on json.org will provide minimal JSON parsing an return a JSONObject that you can query.
I very much doubt that
you can come up with a faster parsing of your json responses using regular expressions or otherwise, without taking the risk of failing in corner cases
given the size of your response strings, that the JSON parsing is the performance bottleneck in your code
Unless you have done some serious profiling, I would play safe and follow the first rule of program optimization
Related
I am getting this response from the server {"status":"true","msg":"success"}
I am trying to parse this json string using Jackson parser library but somehow I am facing mapping-exception stating
com.fasterxml.jackson.databind.JsonMappingException: No content to map due to end-of-input
at [Source: java.io.StringReader#421ea4c0; line: 1, column: 1]
Why do we get this kind of exceptions?
How to understand what is causing this exception?
I am trying to parse using following way:
StatusResponses loginValidator = null;
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(Feature.AUTO_CLOSE_SOURCE, true);
try {
String res = result.getResponseAsString();//{"status":"true","msg":"success"}
loginValidator = objectMapper.readValue(result.getResponseAsString(), StatusResponses.class);
} catch (Exception e) {
e.printStackTrace();
}
StatusResponse class
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({ "status","msg" })
public class StatusResponses {
#JsonProperty("status")
public String getStatus() {
return status;
}
#JsonProperty("status")
public void setStatus(String status) {
this.status = status;
}
#JsonProperty("msg")
public String getMessage() {
return message;
}
#JsonProperty("msg")
public void setMessage(String message) {
this.message = message;
}
#JsonProperty("status")
private String status;
#JsonProperty("msg")
private String message;
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
#JsonGetter
public Map<String, Object> getAdditionalProperties() {
return additionalProperties;
}
#JsonSetter
public void setAdditionalProperties(Map<String, Object> additionalProperties) {
this.additionalProperties = additionalProperties;
}
}
import com.fasterxml.jackson.core.JsonParser.Feature;
import com.fasterxml.jackson.databind.ObjectMapper;
StatusResponses loginValidator = null;
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(Feature.AUTO_CLOSE_SOURCE, true);
try {
String res = result.getResponseAsString();//{"status":"true","msg":"success"}
loginValidator = objectMapper.readValue(res, StatusResponses.class);//replaced result.getResponseAsString() with res
} catch (Exception e) {
e.printStackTrace();
}
Don't know how it worked and why it worked? :( but it worked
In my case the problem was caused by my passing a null InputStream to the ObjectMapper.readValue call:
ObjectMapper objectMapper = ...
InputStream is = null; // The code here was returning null.
Foo foo = objectMapper.readValue(is, Foo.class)
I am guessing that this is the most common reason for this exception.
I could fix this error. In my case, the problem was at client side. By mistake I did not close the stream that I was writing to server. I closed stream and it worked fine. Even the error sounds like server was not able to identify the end-of-input.
OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream());
out.write(jsonstring.getBytes());
out.close() ; //This is what I did
The problem for me was that I read the response twice as follows:
System.out.println(response.body().string());
getSucherResponse = objectMapper.readValue(response.body().string(), GetSucherResponse.class);
However, the response can only be read once as it is a stream.
I had a similar error today and the issue was the content-type header of the post request. Make sure the content type is what you expect. In my case a multipart/form-data content-type header was being sent to the API instead of application/json.
This error is sometimes (often?) hiding the real problem: a failure condition could be causing the content to be incorrect, which then fails to deserialize.
In my case, today, I was making HTTP calls and (foolishly) omitted to check the HTTP status code before trying to unmarshal the body of the response => my real problem was actualy that I had some authentication error, which caused a 401 Unauthorized to be sent back to me, with an empty body. Since I was unmarshalling that empty body directly without checking anything, I was getting this No content to map due to end-of-input, without getting any clue about the authentication issue.
I got this error when sending a GET request with postman.
The request required no parameters.
My mistake was I had a blank line in the request body.
In my case I was reading the stream in a jersey RequestEventListener I created on the server side to log the request body prior to the request being processed. I then realized that this probably resulted in the subsequent read to yield no string (which is what is passed over when the business logic is run). I verified that to be the case.
So if you are using streams to read the JSON string be careful of that.
A simple fix could be Content-Type: application/json
You are probably making a REST API call to get the response.
Mostly you are not setting Content-Type: application/json when you the request.
Content-Type: application/x-www-form-urlencoded will be chosen which might be causing this exception.
I know this is weird but when I changed GetMapping to PostMapping for both client and server side the error disappeared.
Both client and server are Spring boot projects.
Please Don't overthink this issue, simply handle the null pointer exception here as your response is received null and you will get your solution.
In my case I had race condition between 2 threads trying to write and read the file simultaneously. I added the "synchronized" keyword on my methods which writes and reads to the file and the error is gone.
For one, #JsonProperty("status") and #JsonProperty("msg") should only be there only when declaring the fields, not on the setters and geters.
In fact, the simplest way to parse this would be
#JsonAutoDetect //if you don't want to have getters and setters for each JsonProperty
public class StatusResponses {
#JsonProperty("status")
private String status;
#JsonProperty("msg")
private String message;
}
I am working on a project in which I am making a call to one of my servers using RestTemplate which is running a restful service and getting the response back from them.
The response that I will be getting from my server can be either of these error responses (that's all I have for error response) if something has gone wrong -
{"warning": "user_id not found", "user_id": some_user_id}
{"error": "user_id for wrong partition", "user_id": some_user_id, "partition": some_partition}
{"error": "missing client id", "client_id":2000}
or below successful response (it can be any random json string key can also be different) -
{"#data": {"oo":"1205000384","p":"2047935"}
If I am getting any error response as mentioned above, then I am deserializing it (my bad :( ) so that I can log them as an error with a specific error or warning I got front the server which can be for example - user_id not found or missing client id.
If it is a successful response then also I am deserializing it which I don't need for my use case as we don't have any POJO and I just need to return the response as it is which I have got from the server.
In my use case, I don't need to deserialize my response string if it is a successful response as we don't have any POJO for that and we are returning the response string as it is which we have got from the server. But just for logging specific error messages (if I am getting error response from the server) I am deserializing it which I am thinking is unnecessary. There might be better solution for my use case.
Below is my Java client which is calling Callable task using future.get -
public class TestingClient implements IClient {
private ExecutorService service = Executors.newFixedThreadPool(10);
private RestTemplate restTemplate = new RestTemplate();
#Override
public String executeSync(ClientKey keys) {
String response = null;
try {
ClientTask ClientTask = new ClientTask(keys, restTemplate);
Future<String> future = service.submit(ClientTask);
response = handle.get(keys.getTimeout(), TimeUnit.MILLISECONDS);
} catch (TimeoutException e) {
} catch (Exception e) {
}
return response;
}
}
And now below is my ClientTask class which implements Callable interface. In the call method, I am generating an URL and then hit the server using RestTemplate and get the response back -
class ClientTask implements Callable<String> {
private ClientKey cKeys;
private RestTemplate restTemplate;
public ClientTask(ClientKey cKeys, RestTemplate restTemplate) {
this.restTemplate = restTemplate;
this.cKeys = cKeys;
}
#Override
public String call() throws Exception {
// .. some code here
String url = "some_url";
String response = restTemplate.getForObject(url, String.class);
String test = checkJSONResponse(response);
return test;
}
private String checkJSONResponse(final String response) throws Exception {
// may be there are some better way of doing it for my scenario instead of using GSON
Gson gson = new Gson();
String str = null;
JsonObject jsonObject = gson.fromJson(response, JsonObject.class); // parse it, may be performance issues here/
if (jsonObject.has("error") || jsonObject.has("warning")) {
final String error = jsonObject.get("error") != null ? jsonObject.get("error").getAsString() : jsonObject
.get("warning").getAsString();
// log specific `error` here using log4j
str = response;
} else {
str = response;
}
return str;
}
}
As you can see in my above code we are deserializing the JSON string only to log specific error messages if we are getting any error response back. But for successful response we don't need any deserialization but still we are doing it.
Is there any better way of solving this problem? Because currently I am seeing some performance issues with the GSON deserialization.
The only way I can identify successful response along with error response is with error or warning in the response so I am thinking of using regular expressions which can identify error or warning as the key in the response string. If they contain error or warning in the response string then extract the specific error or warning message and log it.
I guess there might be some better way of solving this problem without paying the cost for deserialization.
Just relying on a regex is I think to dangerous. What if the server slightly changes the output format?
I would try to make a quick test, possibly with a simple regexp looking for the string "error" and if there is a chance that it is an error response do a full deserialization to determine if it really was an error or not.
You would pay the extra cost only for false positives when a regular response by chance triggers the quick check.
I would use http codes to control success/fail data parsing.
I need to implement robospice for doing the networking part in my Translator app. I previously used async task class and it was working fine, but now i want to improve my application with implementing robospice. I'am trying to execute the following code but it doesn't't throw any exception it just never executes....
#Override
public TranslatedText loadDataFromNetwork() throws Exception {
String jsonString = getJsonString();
String headerValue = getHeaderValue(jsonString);
String text = pair.getWordPairs().getWordFrom();
String languageFrom = pair.getLanguagePairs().getLanguageFrom().getCode();
String languageTo = pair.getLanguagePairs().getLangougateTo().getCode();
String uri = String
.format("http://api.microsofttranslator.com/v2/Http.svc/Translate?text=%s&from=%s&to=%s&contentType=text/html",
URLEncoder.encode(text, "UTF-8"),
URLEncoder.encode(languageFrom, "UTF-8"),
URLEncoder.encode(languageTo, "UTF-8"));
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", headerValue);
// Create a new RestTemplate instance
RestTemplate restTemplate = new RestTemplate();
// Add the Simple XML message converter
getRestTemplate().getMessageConverters().add(new SimpleXmlHttpMessageConverter());
//set the headerValue in the Entity
org.springframework.http.HttpEntity<?> request = new org.springframework.http.HttpEntity<Object>(headerValue);
// Make the HTTP GET request, marshaling the response from XML to an
// EventList
Log.v("request","Making request!");
//This line never finish execuitng, doesen't throw exception or anything in logCat
ResponseEntity<Object> responseEntity = getRestTemplate().exchange(uri, HttpMethod.GET, request, null);
Log.v("request", responseEntity.getBody().toString());
Log.d("Load Data From Network", request.getBody().toString());
return null;
}
The last thing it shows in log cat is Request First!! And nothing after that. It never even gets to The Request Listener onRequestFailure.
Can any 1 tell me what i do wrong ?
This is what look weird to me in your code:
ResponseEntity<Object> and null as 4th parameter of the exchange method are not correct. You need to provide a type which represents the response you get from the server.
The object returned by loadDataFromNetwork() is what you will get in the onRequestSuccess() method. Returning null is not a good idea, in my opinion.
I fixed the problem. So if you need to handle stream you will have to provide the following code
ResponseEntity<byte[]> responseEntity = getRestTemplate().exchange(uri, HttpMethod.GET, request, byte[]);
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.
I would like to parse a local JSON file and marshal it into models using RestTemplate, but can't tell if this is possible.
I'm trying to pre-populate a database on an Android app that is using RestTemplate for syncing with the server. Rather than parsing the local JSON on my own, I thought, why not use RestTemplate? It's made exactly for parsing JSON into models.
But...I can't tell from the docs if there is any way to do this. There is the MappingJacksonHttpMessageConverter class which appears to convert the server's http response into a model...but is there any way to hack that to work with a local file? I tried, but kept getting deeper and deeper down the rabbit hole with no luck.
Figured this out. Instead of using RestTemplate, you can just use Jackson directly. There is no reason RestTemplate needs to be involved in this. It's very simple.
try {
ObjectMapper mapper = new ObjectMapper();
InputStream jsonFileStream = context.getAssets().open("categories.json");
Category[] categories = (Category[]) mapper.readValue(jsonFileStream, Category[].class);
Log.d(tag, "Found " + String.valueOf(categories.length) + " categories!!");
} catch (Exception e){
Log.e(tag, "Exception", e);
}
Yes, I think it is possible(with MappingJacksonHttpMessageConverter).
MappingJacksonHttpMessageConverter has method read() which takes two parameters: Class and HttpInputMessage
MappingJacksonHttpMessageConverter converter = new MappingJacksonHttpMessageConverter();
YourClazz obj = (YourClazz) converter.read(YourClazz.class, new MyHttpInputMessage(myJsonString));
With this method you can read single object from single json message, but YourClazz can be some collection.
Next, You have to create you own HttpInputMessage implementation, in this example it expected json as string but You probably can pass stream to your json file.
public class MyHttpInputMessage implements HttpInputMessage {
private String jsonString;
public MyHttpInputMessage(String jsonString) {
this.jsonString = jsonString;
}
public HttpHeaders getHeaders() {
// no headers needed
return null;
}
public InputStream getBody() throws IOException {
InputStream is = new ByteArrayInputStream(
jsonString.getBytes("UTF-8"));
return is;
}
}
PS. You can publish your app with database