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;
}
}
Related
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.
I have a Json of this type :
{
"4f958ef28ecd651095af6ab6": {
enormous JsonObject
}
}
the "4f958ef28ecd651095af6ab6" is different each time (but I know what it will be as it is a parameter of my api call), it corresponds to the id of the following object. I have a Gson-configured model to parse the enormous JsonObject.
My question is : is it performant to use simply
new JSONObject(jsonresponse).getJSONObject("4f958ef28ecd651095af6ab6")
and parse with Gson from there ?
Is there a better way to do so ?
I guess the real question would be, what does "new JSONObject(String)" realy do ?
Thanks
What you are doing is:
You load all the Json string into the phone memory (memory issue + long time to load entirely)
You create a big JSONObject (same issues) in order to have access to each key.
You write few code but this is not the most performant solution.
To minimized the memory impact and accelerate the operation of objects' creation, you can use Gson in stream mode.
By directly read the input stream, you avoid to load too much data and you can directly start to populate your object piece by piece.
And about the JSONObject, it will mostly check if your json string is correct (or it will throw a JsonException) and it will let you look into the object when you search for a key and its value.
I would recommend use hybrid (native and gson) since i am not sure how to get unknown jsonobject with GSON.
You need to get your response as a JSONArray, then itarete for each JSONObject. You can experiment parsing code as trying. Please check JSONArray.getJSONObject(int index) method. Then we can use GSON to get our data model to get known attributes.
If you can post complete json data, we can give it chance to parse together.
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 to maintain a local store of a database, accessible via a REST API. I am attempting to use the fantastic RoboSpice and Spring libraries for the REST client, Jackson to parse/cache the JSON response and ORMLite to persist the resulting objects.
The problem is that I don't know how to store foreign object relationships for the JSON responses I get. Sometimes the JSON objects are nested, other times they are referenced by Id.
A typical systems response
{
id:567,
name:"The only system",
competitions:[{
id:123,
system_id:567
...
}];
}
A competitions response
items: {
123:{
id:123,
system_id:567 // System only referenced by id
...
}}
Another competitions response
items: {
123:{
id:123,
system_id:567, // System referenced by id and nested
system:{
id:567,
name:"The only system",
...
}
}
}
Nested objects work fine, but it's the cases where there is a reference by id that is killing it. I have two classes for my data model as follows.
Systems class
#DatabaseTable("systems")
public class System {
#JsonProperty("id")
#DatabaseField(id=true, columnName="id")
private long id;
#JsonProperty("name")
#DatabaseField(columnName="name")
private String name;
#JsonProperty("competitions")
#ForeignCollectionField
private ForeignCollection<Competition> competitions;
// getters & setters omitted
}
Competition class
#DatabaseTable("competitions")
public class Competition {
#JsonProperty("id")
#DatabaseField(id=true,columnName="id")
private long id;
#JsonProperty("system_id")
#DatabaseField(columnName="id")
private long systemId; // This is definitely at least part of the problem
#JsonProperty("system")
#DatabaseField(foreign=true, columnName="system_id")
private System system;
// getters & setters omitted
}
Having two properties references the system_id seems like a definitively bad idea, but I can't find an alternative for the behaviour I want. Even if the system object is not nested in the Competition object, the Competition should be able to map a relationship to the System object in the local database, because the id of the system is always provided.
The other problem I suspect is that I'm using two object persisters, the JacksonObjectPersister and the InDatabaseObjectPersisterFactory (provided by RoboSpice for use with ORMLite). The reason for this is because I don't want to persist the lists that the objects are nested in to the database. By my understanding, Jackson should cache the JSON response from the server, ORMLite should cache the data model.
This is the error I'm getting
02-03 15:15:57.640: D//DefaultRequestRunner.java:166(20944): 15:15:57.636 Thread-28
An exception occurred during service execution :org.codehaus.jackson.map.JsonMappingException:(was java.lang.NullPointerException)
(through reference chain: com.company.app.api.objects.List["items"]->
java.util.HashMap["51"]->
com.company.app.api.objects.Competition["system_id"])
Apologies for what seems like a number of questions in one, I am bashing my head against a wall. I will attempt to wrap this essay up with a summary...
Is there any way to piece together object relationships using Jackson and ORMLite for the JSON responses I have provided?
I've been searching for the past week on how to develop an android project, read some on android developers page and on other websites like here, but no text was complete.
i have this project - i'm a php developer not a java, but could understand a bit about java lately.
the thing is i want to develop an android app using my website, i did output a json type data from my website, and gonna use them on the android app, i did the async request on android and could read the data form the website but :
first question: how to parse the json data correctly and convert it to array on android, i did that through:
Iterator<String> itr = myObject.keys();
while (itr.hasNext()) {
...
i don't know if that's the correct way, when i try to convert my json object to array, it gives me type mismatch.
second and more importantly:
how can create a "Block" like facebook posts style, or twitter style blocks, you know - blocks of json data, is it a linearlayout ? what do i call it ? and how can i add it to the UI dynamically, cuz these blocks are pulled from the website json data. so they are arrays...of blocks..
i'm kinda confused still, i need a start point.
Thank you!
excellent tutorial for beginners for android development
http://thenewboston.org/list.php?cat=6
and for your first question - how to parse json data correctly,
you can try using gson to convert the json data into POJO
otherwise you'd have to do myObject.opt(key) to make sure it is there
First question: you should use a library to parse JSON, it's simpler that way. Try gson. You should create a class, which holds the parsed object, like:
public class Taxi implements Serializable {
private static final long serialVersionUID = 1L;
#SerializedName("idTaxi")
private Integer idTaxi;
#SerializedName("name")
private String name;
//getter, setters, constructor, etc
}
Then when you get the JSON object, you can parse it:
Gson gson = new Gson();
Reader reader = new InputStreamReader(SOURCE_STREAM);
Taxi[] response = gson.fromJson(reader, Taxi[].class);
Second question: i think a ListView would be good for you. You can read a full tutorial about it here