Json Get/Send Data Strategies - java

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.

Related

Passing JSON Body to a REST call in java

I am having trouble in executing a post call from java. However I am able to execute the same from Postman.
For my rest call content body should be like this
{"group": "group1","users": ["Z123456","a123456","x123456"]}
For this I created a pojo like this:
public class GroupUserMapping {
String group;
ArrayList<String> users;
}
And In code I created a method to add objects to this pojo like this
ArrayList<GroupUserMapping> usergroups = new ArrayList<>();
//some conditions
GroupUserMapping groupUserMapping = new GroupUserMapping(group,users);
usergroups.add(groupUserMapping);
Now for all this objects I need to call the rest API
usergroups.stream().parallel().forEach(ausergroup -> {
try {
CloseableHttpClient client = HttpClients.createDefault();
HttpPost httpPost = new HttpPost(url);
Gson gson = new Gson();
String base64 = basicEncode();
httpPost.addHeader("Authorization", base64);
httpPost.setHeader("Content-type", "application/json");
StringEntity entity = new StringEntity(gson.toJson(ausergroup.toString()));
httpPost.setEntity(entity);
HttpResponse response = client.execute(httpPost);
} catch (Exception e) {
e.printStackTrace();
}
});
after executing this I am getting 400 error code. Please help me in solving this?
Thank you.
I changed ausergroup.toString() to ausergroup. IT worked fine for me. However I have a new problem now.
When I am adding elements to pojo every time the group is adding as new element but users list is updating for all groups. Ideally it should be different for each group but after adding all elements I am seeing users are same for all groups. Where I am doing wrong?

Efficient way to detect strings?

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

How to use regular expressions to identify the error message in the JSON String?

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.

Java convert Json array to typed List<T>

I have a webservice that sends a typed arraylist which I capture via HttpResponse like so:
// create GET request
HttpGet httpGet = new HttpGet("http://localhost:8084/MinecraftRestServer/webresources/Items");
// execute GET request
HttpResponse response = client.execute(httpGet);
// check response
StatusLine statusLine = response.getStatusLine();
int statusCode = statusLine.getStatusCode();
if (statusCode == 200) { // response OK
// retreive response
List<Recipe> recipesList = new ArrayList<Recipe>();
HttpEntity jsonObj = response.getEntity();
//What's next?
The array that's being sent from the webservice looks like this:
recipesList.add(new Item(1, 11, "diamond_ingot", "Diamond ingot",
"0,0,0,0,0,0,0,0,1", "air,diamond_ore"));
recipesList.add(new Item(2, 11, "iron_ingot", "Iron ingot",
"0,0,0,0,0,0,0,0,1", "air,iron_ore"));
And comes out in this format:
[{"recipeCategory":11,"recipeImageID":"diamond_ingot","recipeDescription":"Diamond ingot","recipeLocations":"0,0,0,0,0,0,0,0,1","usedImages":"air,diamond_ore","recipeID":1},{"recipeCategory":11,"recipeImageID":"iron_ingot","recipeDescription":"Iron ingot","recipeLocations":"0,0,0,0,0,0,0,0,1","usedImages":"air,iron_ore","recipeID":2},{"recipeCategory":11,"recipeImageID":"gold_ingot","recipeDescription":"Gold ingot","recipeLocations":"0,0,0,0,0,0,0,0,1","usedImages":"air,gold_ore","recipeID":3},{"recipeCategory":11,"recipeImageID":"diamond_ore","recipeDescription":"Diamond ore","recipeLocations":"0,0,0,0,0,0,0,0,1","usedImages":"air,wooden_pickaxe","recipeID":4},{"recipeCategory":11,"recipeImageID":"iron_ore","recipeDescription":"Iron ore","recipeLocations":"0,0,0,0,0,0,0,0,1","usedImages":"air,wooden_pickaxe","recipeID":5},{"recipeCategory":11,"recipeImageID":"gold_ore","recipeDescription":"Gold ore","recipeLocations":"0,0,0,0,0,0,0,0,1","usedImages":"air,wooden_pickaxe","recipeID":6},{"recipeCategory":2,"recipeImageID":"diamond_boots","recipeDescription":"Boots (Diamond)","recipeLocations":"0,0,0,1,0,1,1,0,1","usedImages":"air,diamond_ingot","recipeID":7},{"recipeCategory":2,"recipeImageID":"gold_boots","recipeDescription":"Boots (Gold)","recipeLocations":"0,0,0,1,0,1,1,0,1","usedImages":"air,gold_ingot","recipeID":8},{"recipeCategory":2,"recipeImageID":"iron_boots","recipeDescription":"Boots (Iron)","recipeLocations":"0,0,0,1,0,1,1,0,1","usedImages":"air,iron_ingot","recipeID":9},{"recipeCategory":2,"recipeImageID":"diamond_leggings","recipeDescription":"Leggings (Diamond)","recipeLocations":"1,1,1,1,0,1,1,0,1","usedImages":"air,diamond_ingot","recipeID":10},{"recipeCategory":2,"recipeImageID":"gold_leggings","recipeDescription":"Leggings (Gold)","recipeLocations":"1,1,1,1,0,1,1,0,1","usedImages":"air,gold_ingot","recipeID":11},{"recipeCategory":2,"recipeImageID":"iron_leggings","recipeDescription":"Leggings (Iron)","recipeLocations":"1,1,1,1,0,1,1,0,1","usedImages":"air,iron_ingot","recipeID":12},{"recipeCategory":2,"recipeImageID":"diamond_chestplate","recipeDescription":"Chestplate (Diamond)","recipeLocations":"1,0,1,1,1,1,1,1,1","usedImages":"air,diamond_ingot","recipeID":13},{"recipeCategory":2,"recipeImageID":"gold_chestplate","recipeDescription":"Chestplate (Gold)","recipeLocations":"1,0,1,1,1,1,1,1,1","usedImages":"air,gold_ingot","recipeID":14},{"recipeCategory":2,"recipeImageID":"iron_chestplate","recipeDescription":"Chestplate (Iron)","recipeLocations":"1,0,1,1,1,1,1,1,1","usedImages":"air,iron_ingot","recipeID":15},{"recipeCategory":2,"recipeImageID":"diamond_helmet","recipeDescription":"Helmet (Diamond)","recipeLocations":"1,1,1,1,0,1,0,0,0","usedImages":"air,diamond_ingot","recipeID":16},{"recipeCategory":2,"recipeImageID":"gold_helmet","recipeDescription":"Helmet (Gold)","recipeLocations":"1,1,1,1,0,1,0,0,0","usedImages":"air,gold_ingot","recipeID":17},{"recipeCategory":2,"recipeImageID":"iron_helmet","recipeDescription":"Helmet
My question is, how can I convert this back into an arraylist (ArrayList<Item>)
There is already an Item class present in the client application.
I've read examples about the Gson library but it seems it's not included anymore when compiling in API 17.
What would be the easiest approach?
Download and include GSON jar from here in your project if using Eclipse.
If using Android Studio then open your build.gradle and add the below to your dependencies block. Or again you can choose not to use maven and simply drop the jar in your lib folder.
compile 'com.google.code.gson:gson:2.2.4'
Next, use GSON to construct a list of items.
Make sure you have your Item.java class with same member names as in the JSON response
List<Recipe> recipesList = new ArrayList<Recipe>();
HttpEntity jsonObj = response.getEntity();
String data = EntityUtils.toString(entity);
Log.d("TAG", data);
Gson gson = new GsonBuilder().create();
recipesList = gson.fromJson(data, new TypeToken<List<Item>>() {}.getType());
Make sure you handle the exceptions appropriately.
You could use Jackson to parse the incoming JSON. (Quick introduction)
If you already have a Class with the appropriate properties, it can be as easy as something like this:
public class Items {
private List<Item> items;
// getter+setter
}
ObjectMapper mapper = new ObjectMapper();
Items = mapper.readValue(src, Items.class);
See this for more information.
Step 1 : Item obj=new Item;
Step 2: Parse the json formar for example here :
[[Example1][1]
Step 3: while parsing put ur values in obj :
obj.recipeCategory=value1;
Step 4: insret ur obj into arraylist:
arrayList.add(obj);
I think you should using json-simple library to parse string Json to JsonObject and convert to simple data type.
Example:
JSONArray arrJson = (JSONArray) parser.parse("String json");
Get each element JSONObject in JSONArray, then parse it to simple data type:
long recipeCategory = (long) jsonObject.get("recipeCategory");
You can use Gson like many users said, here is an example of a RESTfull client using Gson:
public class RestRequest {
Gson gson = new Gson();
public <T> T post(String url, Class<T> clazz,
List<NameValuePair> parameters) {
// Create a new HttpClient and Post Header
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost(url);
try {
// Add your data
httppost.setEntity(new UrlEncodedFormEntity(parameters));
// Execute HTTP Post Request
HttpResponse response = httpclient.execute(httppost);
StringBuilder json = inputStreamToString(response.getEntity()
.getContent());
T gsonObject = gson.fromJson(json.toString(), clazz);
return gsonObject;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
// Fast Implementation
private StringBuilder inputStreamToString(InputStream is)
throws IOException {
String line = "";
StringBuilder total = new StringBuilder();
// Wrap a BufferedReader around the InputStream
BufferedReader rd = new BufferedReader(new InputStreamReader(is));
// Read response until the end
while ((line = rd.readLine()) != null) {
total.append(line);
}
// Return full string
return total;
}
}
The usage will be something like this:
new RestRequest("myserver.com/rest/somewebservice", SomeClass.class, Arrays.asList(new BasicValuePair("postParameter", "someParameterValue")));
Where SomeClass.class will be Recipe[].class in your case. Also check this question to properly handle server side errors.
Man, google is your friend! A quick search for "android json" or "android json parse" gives you some nice tutorials like this one or this here.

Parse local JSON file with RestTemplate

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

Categories