I've a response class
public class ResponseModel<T> {
private boolean isRequestSuccessful;
public boolean getIsRequestSuccessful() {
return this.isRequestSuccessful;
}
public void setIsRequestSuccessful(boolean isRequestSuccessful) {
this.isRequestSuccessful = isRequestSuccessful;
}
private String message;
public String getMessage() {
return this.message;
}
public void setMessage(String message) {
this.message = message;
}
private T object;
public T getObject() {
return this.object;
}
public void setObject(T object) {
this.object = object;
}
}
My API will return type T. I would like to parse the response from the API and create a object of type ResponseModel.
I am trying to achieve something like below which I can do it easily with c#. Please help on how to do this with Java
public static ResponseModel<T> Get(String requestUri) throws ClientProtocolException,IOException {
CloseableHttpClient client = HttpClientBuilder.create().build();
HttpGet httpGet = new HttpGet(requestUri);
httpGet.addHeader("TenantKey", TenantKey);
httpGet.addHeader("accept", "application/json");
HttpResponse response = client.execute(httpGet);
ResponseModel<T> responseModel = new ResponseModel<T>();
if (response.getStatusLine().getStatusCode() == 200) {
Gson gson = new GsonBuilder().create();
// parse the response as T and and assign to object of ResponseModel
responseModel.object = ...
}
else
{
responseModel.message = response.getEntity().getContent();
}
// return ResponseModel here
}
Generics in C# and Java are pretty different. Simply spoken, there is no sense in what you are doing here.
The java generic T you are using there is a compile time feature. It allows you to use more specific types at compile time, instead of using Object all over the place.
Therefore you can't use generics to determine a "T" at "runtime", as you probably intend to. That T in your method comes from the "outside", and the compiler determines that in occasion it should be a ResponseModel<Integer> and ResponseModel<Whatever> in another context.
You can't have gson read JSON data to return a specific ResponseModel<Whatever> for you. If at all, you might be able to use TypeAdapter magic that does some switching based on the actual value, to return this or that specific ResponseModel<Foo>.
Beyond that: when using such bean like classes as your ResponseModel, you simply want them to be specific, not generic.
Not sure, but I had a similar requirement, but its for Android though. Here is the reference link, where I had to write a generic class to load the different forms of JSON from Assets folder and parse to POJO class.
https://github.com/gokulnathperiasamy/Android-Helper/blob/master/JSONHelper.java
Code:
private static String convertJSONtoEntity(String jsonString, String typeString) {
String jsonObjectString = null;
try {
JSONObject jsonObject = new JSONObject(jsonString);
jsonObjectString = jsonObject.get(typeString).toString();
} catch (Exception e) {
Log.e(TAG, e.getLocalizedMessage());
}
return jsonObjectString;
}
private static <T> List<T> fromJsonList(String json, Class<T> clazz) {
Object[] array = (Object[]) java.lang.reflect.Array.newInstance(clazz, 0);
array = new Gson().fromJson(json, array.getClass());
List<T> list = new ArrayList<>();
if (array != null && array.length > 0) {
for (Object anArray : array) {
list.add(clazz.cast(anArray));
}
}
return list;
}
Usage:
Invoke convertJSONtoEntity() with your jsonString and typeString will be your root element of your JSON.
Invoke the fromJsonList() with value returned by convertJSONtoEntity() and Class. This gives list of objects from JSON.
With the help of other answers and further searching in Google, I ended up with the following code
public static <T> ResponseModel<T> Get(Class<?> classType, String requestUri) throws ClientProtocolException, IOException
{
CloseableHttpClient client = HttpClientBuilder.create().build();
HttpGet httpGet = new HttpGet(requestUri);
httpGet.addHeader("accept", "application/json");
HttpResponse response = client.execute(httpGet);
ResponseModel<T> responseModel = new ResponseModel<T>();
if (response.getStatusLine().getStatusCode() == 200)
{
responseModel.setObject((T) Utils.fromJson(EntityUtils.toString(response.getEntity()), classType));
responseModel.setIsRequestSuccessful(true);
}
else
{
responseModel.setMessage(response.getEntity().getContent().toString());
responseModel.setIsRequestSuccessful(false);
}
return responseModel;
}
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 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.