In one of my apps I have a generic Volley web service which I use to load data from our servers and deserialize them sing GSON like so:
#Override
protected Response<T> parseNetworkResponse(NetworkResponse response) {
try {
String json = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
T parsedJson = mGson.fromJson(json, mType);
return Response.success(parsedJson, HttpHeaderParser.parseCacheHeaders(response));
} catch (Exception e) {
return Response.error(new ParseError(e));
}
}
Now one of the objects I'm deserializing is a little bit special since its json model contains of two lists but in code I need a dictionary where ids of the first lists objects are the keys and arrays of the second lists objects are the values.
I also need to sort the two lists after they have been deserialized. (I begged our backend devs to do the sorting and deliver me a proper json model but they refuse to do that, don't ask me why...).
Anyways, now I need a way to do the sorting as well as the calculation of the properties.
In iOS I'm using SwiftyJson which is not really the automatic JSON deserialization you'd get from GSON but there I can calculate and sort the necessary fields in the background thread where I "deserialize" the obejcts.
On Android though, I have this generic function and therefore no clue which object I'm currently deserializing and as far as I'm concerned, GSON uses a default constructor and writes to the fields directly rather than using setters.
So now I'm stuck. I wonder when the best situation would be to calculate my fields. I thought about these ways:
Add a transient boolean to check if I already sorted the lists. When accessing the getter for the lists, the first time it is false so I know I have to sort. I sort the list, store the sorted list back and then return it. For the calculated dictionary, I'd just had to check if its null and if so, calculate it
Implement some sort of PostDeserializable interface with a single method. In the generic web service, I could check if the T parsedJson is an instance of that interface and if so, call the method on it.
The later would had the advantage that it would run in the background but I also had to remember that interface (which might be problematic for new developers who don't know about it). The first however could have an impact since it most likely would run in the UI thread.
I wonder if there is a default way in GSON to execute some post serialization method, maybe via annotations? Hope you can help me find the best way with the least amount of custom code.
Okay so apparently there is no default way of doing it. Though there is a similar interface to the one I suggested in my original post but it is only available in the gson-extras which are not published via maven so one can not simply include it in the gradle file. Instead, one would have to manually download it and keep it up to date.
For me that seems to be unnecessary work and including the whole gson-extras for just this one interface seems to be a little bit over the top. After testing both of my approaches, I think I will stick with the interface solution I suggested.
I changed my JSON volley base request (from which all of my JSON requests inherit) to the following:
#Override
protected Response<T> parseNetworkResponse(NetworkResponse response) {
try {
String json = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
T parsedJson = mGson.fromJson(json, mType);
if (parsedJson instanceof PostDeserializable) {
PostDeserializable postDeserializable = (PostDeserializable) parsedJson;
postDeserializable.postDeserialization();
}
if (parsedJson instanceof Object[]) {
Object[] array = (Object[]) parsedJson;
for(Object value : array) {
if (value instanceof PostDeserializable) {
PostDeserializable postDeserializable = (PostDeserializable) value;
postDeserializable.postDeserialization();
}
}
}
return Response.success(parsedJson, HttpHeaderParser.parseCacheHeaders(response));
} catch (Exception e) {
return Response.error(new ParseError(e));
}
}
The PostSerializable interface looks pretty simple as well:
public interface PostDeserializable {
void postDeserialization();
}
And to use it, all I have to do is to implement it on my model POJOs. Notice that I check if the T parsedJson itself is an instance of my interface as well as I check if it is an array and if so, if the objects in the array are of that type.
For me that is enough because I only every have the need for a PostSerializable if the top level object is an instance of it or if the top level object is an array of PostSerializables.
This is not a general solution because it does not cover cases, where the PostSerializable is for example in the 2nd level of another model.
In such a case, I think it would definitely make sense to use gson-extras.
Related
The problem is next.
In response I have JSON like
{
object: {
// a lot of different fields
}
}
I use Retrofit with gson parser. What I really need is just this object. I don't want to create class for response with the only one field. All responses server send in a such manner. As far I understand somewhere I need place simple code for fetching that one object and then use default parser for it.
Probably sorry for stupid question. I used Volley and there was quite a different approach.
Instead of creating a special class to handle this (and another special class for every other server response), just use Map<String, YourRealObjectType>. Then use this method to extract the YourRealObjectType instance for each response:
public static <T> T getFirstValue(Map<String, T> map) {
return map.values().iterator().next();
}
you can convert class into JsonObject class. then cal iterate all the elements in it one by one
#Get
ObservablegetData();
Note : use JsonObject not JSONObject
I'm using retrofit to handle rest-api calls.
I have a rest API that returns the following json
"MyObject": {
"43508": {
"field1": 4339,
"field2": "val",
"field3": 15,
"field4": 586.78
},
"1010030": {
"field1": 1339,
"field2": "val212",
"field3": 1,
"field4": 86.78
},...
}
Please notice that the object MyObject contains objects with a name that is actually an id.
For all the other rest APIs I'm using retrofit without problems.
In this case it seems not possible to use the standard approach: defining a class containing the fields expected in the response.
Is there a way to transform this json into a json containing an array of
{
"field1": xxx,
"field2": "yyy",
"field3": www,
"field4": zzz
}
Or is there a better way to deal with this problem without going back to "manually" parsing the json?
Try to use next approach:
public class Response {
Map<String, YourObject> MyObject;
// getter, setter
}
public interface GitHubService {
#GET("some_path")
Call<Response> listMyObjects();
}
All you objects will be parsed to Map. You can get the list of all ids via keySet() method or list all entries with entrySet().
Try putting the annotation, SerializedName(nameOfField) over the variable name.
#SerializedName("13445345")
MyObject object;
Well my idea is quite manual, but it should work. I will not copy and paste another person's answer here, so take a look at this answer to see how to loop through all of the myObject's keys. Then for each of those keys, make a new JSONArray and add key value pair of fieldX-valueX.
This is just a basic idea, since I think you can handle the code yourself, you seem like a guy who knows his way around the simple stuff.
I'm using Volley Library to get JSON responses on my requests. I created base http Helper classes which process requests and return JSON for next processing.
I would like to ask what is the right approach to process returned JSON data?
I would like to use data for displaying in ListView, View, etc. but I don't know what is the right approach (Convert to POJO or keep data in JSON?)
I tried to find any solution on this topic:
Json to POJO mapping in Android
But it seems that each object should have a single class with definition of the all possible fields of the object.
This approach seems strange to me because I have already created models for the database objects and if JSON object is changed (for example added new attribute or changed his name) it means that code on the API models should be changed too.
Is there any other possibility and the right way how to avoid this and work with returned data only in Activities (Controllers)?
Use GSON it is quite snappy and easy to use. for e.g.
Gson gson = new Gson(); // Or use new GsonBuilder().create();
MyType target = new MyType();
String json = gson.toJson(target); // serializes target to Json
MyType target2 = gson.fromJson(json, MyType.class); // deserializes json into target2
Read my answer completely, i've explained in easy way.
First of all if you are using Volley no need of http helper class, use Volley's method to get JSON data by objects or array.
Second POJO classes are best use it. yes it is the right approach.
Here is the source code to get json object data from volley and store in POJO.
/**
* Method to make json object request where json response starts wtih {
* */
private void makeJsonObjectRequest() {
JsonObjectRequest jsonObjReq = new JsonObjectRequest(Method.GET,
"http://api.example.com", null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
Log.d(TAG, response.toString());
try {
// Parsing json object response
// response will be a json object
String name = response.getString("name");
String email = response.getString("email");
//POJO class to store
Person person = new Person();
person.name=name;
person.email=email;
} catch (JSONException e) {
e.printStackTrace();
Toast.makeText(getApplicationContext(),
"Error: " + e.getMessage(),
Toast.LENGTH_LONG).show();
}
hidepDialog();
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
VolleyLog.d(TAG, "Error: " + error.getMessage());
Toast.makeText(getApplicationContext(),
error.getMessage(), Toast.LENGTH_SHORT).show();
// hide the progress dialog
hidepDialog();
}
});
// Adding request to request queue
AppController.getInstance().addToRequestQueue(jsonObjReq);
}
I would definitely suggest you transform JSON to POJOs and use this. It is much more natural to use objects to represent data in an object oriented language, such as Java.
I would like to ask what is the right approach to processing returned JSON data?
There are two main options:
Use the built - in JSON SDK - it is a very decent tool and is adequate if you don't have complex response structures. It can be quite performant.
Use some kind of a third party JSON processing library - GSON or Jackson. They come stacked with functionality, like automatic deserialisation from JSON to POJO based on annotations, etc. This can save you time, but it is expensive. To get the best performance out of this, you should still parse the things manually, but you can start doing this only if you need to optimise.
No matter what you use, just make sure that JSON processing is done on a worker thread. If you are using Volley, you should consider extending a Request class and overriding parseNetworkResponse() - this is a good place to plug in your deserialisation. The responses will now be POJOs and you can use them to populate lists, etc.
As far as this goes:
This approach seems to me strange because i have already created models for the database objects and if JSON object is changed (for example added new attribute or changed his name) it means that code on the API models should be changed too.
Unfortunately, you are right. But this is a common problem in client - server communication. When using JSON over HTTP there is no way to enforce that the contract between server and client is being followed. The best you could do, IMHO, is detect the possible exceptions and handle them accordingly - showing a message to the user, or something like that. You can always use Maps to hold the deserialised payload as key - value pairs, but this doesn't really solve the issue, but ensures the deserialisation logic wont' fail.
I am trying something very simple. I have a custom API called "missingvehiclesfrominventoryjob" and it simply returns a record set from an standard SQL Query.
I can do this in my WinForms and Windows Phone app easily but I cannot figure out how to do this on the Android App.
Here is my code: (which DOES NOT COMPILE in Android Studio):
msClient.invokeApi("missingvehiclesfrominventoryjob", kd, new
ApiOperationCallback<List<InventoryProspects>>(){
#Override
public void onCompleted(List<InventoryProspects> missingVehicles, Exception e,
ServiceFilterResponse serviceFilterResponse){
for (InventoryProspects item : missingVehicles){
mAdapter.add(item);
}
}
});
The problem is the List in the parameters of the Callback. I am not sure how to indicate that the invoiceAPI call will return multiple rows from the database and I cannot find anywhere in the docs to explain how. Nor can I find an example ANYWHERE on the internet.
I am sure I am not the only on trying to do this.
Thanks in advance
Chuck Giddens
What i did to overcome this problem, is to call a different overload of invokeApi that returns a JsonElement, and then deserialise it into my objects like so:
mClient.invokeApi("MyCustomApi",null, "GET", null, new ApiJsonOperationCallback() {
#Override
public void onCompleted(JsonElement jsonElement, Exception e, ServiceFilterResponse serviceFilterResponse) {
GsonBuilder gsonb = new GsonBuilder();
Gson gson = gsonb.create();
JsonArray array = jsonElement.getAsJsonArray();
List<MyObject> myObjects = new ArrayList<MyObject>()>
for(int i = 0; i < array.size(); i++)
{
myObjects.add(gson.fromJson(array.get(i).getAsJsonObject().toString(), MyObject.class));
}
}
});
I haven't had a chance to test it yet (will try when I have time and edit answer as needed) but my thinking is that the Android SDK won't allow you to do what you're trying to do. The invokeApi methods expect a strongly typed class to be set as the response type (or you can use the raw JSON methods). In this case, you're trying to say you want a list of items back, but I don't think that will work. I think you'll instead need to create a new class (i.e. missingvehiclesfrominventoryjobResponse) which contains a property that is of type List< InventoryProspects>. Note that you'll need to change your method call to actually match one of the available options for invokeApi which I don't believe it's doing right now. You can read more about the different formats of the method here: http://blogs.msdn.com/b/carlosfigueira/archive/2013/06/19/custom-api-in-azure-mobile-services-client-sdks.aspx
Alternatively, you can use the table methods against a table endpoint where the read expects a collection of results back.
Have you tried to remote debug your API call from the app.[http://blogs.msdn.com/b/azuremobile/archive/2014/03/14/debugging-net-backend-in-visual-studio.aspx]. Your app will timed out in doing that but you can see line by line execution of your controller action if it returns the correct result set. If there is no problem with it then the problem should be in parsing result set.
What is the exception you are getting in callback? And have you tried using other method parameters such as passing with different HTTP methods? Use this as a reference as well. http://azure.microsoft.com/en-us/documentation/articles/mobile-services-android-get-started/
Please paste your exception or either controller action, and the object structure of the data transfer object of the result set.
I wondering what folks think about the PROS/CONS of parsing JSON to POJOs vs. using JSON overlay objects.
Use Case: REST call that returns Person JSON documents.
e.g.
[{"name":"name1"}, {"name":"name2}, ...]
Solution 1:
Parse all incoming JSON into POJOs using GSON, Jackson, JSONObject/JSONArray (manual), etc.
Result:
class Person {
String name;
public String getName() {
return name;
}
}
Solution 2:
Create overlay class that uses JSONObject access methods.
Result:
class Person {
JSONObject json;
public String getName() {
json.getString("name");
}
}
In particular, I am interested in terms of limited system ressources on Android. Do I want to incur the parsing cost upfront and use more memory, or do I want to incur the parsing cost during data access such as in a ListView.
Does it matter?
I would usually prefer Solution 1.
Downloading and parsing as a single asynchronous task is pretty simple. Parsing should be quite fast compared to downloading from the web.
The JSON data can be removed from memory after parsing is done and the required memory is lower at that point.
Accessing the data is then faster than in Solution 2 - fast access is essential if you want to use it in the UI thread to e.g. update the items in a ListView.
But there are scenarios where Solution 2 should yield better results. Like when there are thousands of people or the data changes faster than you can parse it. A ListView requests just the data that is currently visible so parsing would be done only on demand and would become more or less independant from the amount of data that exists for invisible items.
And in case parsing a single item takes really long, Solution 2 can also be used with an asynchronous approach like the async image loaders used with ListViews. I would use something like below in that case since that prevents that parsing needs to be repeated for the same object all the time.
class Person {
boolean parsed;
String name;
JSONObject json;
public String getName() {
if (!parsed) {
name = json.getString("name");
json = null;
parsed = true;
}
return name;
}
}