Parsing deep nested JSON data - android
I am newbie learning Android and ran into an issue when trying to parse my API data into my app. It calls fine, but I am unsure why I am getting the error.
Here is the API data that is pulled:
{
"report":{
"sr":"28",
"type":"Basic",
"food":{
"ndbno":"01009",
"name":"Cheese, cheddar",
"ds":"Standard Reference",
"manu":"",
"ru":"g",
"nutrients":[
{
"nutrient_id":"255",
"name":"Water",
"derivation":"NONE",
"group":"Proximates",
"unit":"g",
"value":"37.02",
"measures":[
{
"label":"cup, diced",
"eqv":132,
"eunit":"g",
"qty":1,
"value":"48.87"
}
]
}
]
}
}
}
And here is my code:
JSONArray JA = new JSONArray(macros);
for (int i =0; i <JA.length(); i++){
JSONObject JO = (JSONObject) JA.get(i);
singleParsed = "Name:" + JO.get("report") + "\n" +
"Item:" + JO.get("reports.food.name") + "\n" +
"Nutrient:" + JO.get("reports.food.nutrients.name") + "\n" +
"Serving suggestions:" + JO.get("reports.food.measures.value");
So far that hasn't worked for me.
W/System.err: org.json.JSONException: Value {"report":{"sr":"28","type":"Basic","food":{"ndbno":"01009","name":"Cheese, cheddar","ds":"Standard Reference","manu":"","ru":"g","nutrients":[{"nutrient_id":"255","name":"Water","derivation":"NONE","group":"Proximates","unit":"g","value":"37.02","measures":[{"label":"cup, diced","eqv":132,"eunit":"g","qty":1,"value":"48.87"},{"label":"cup, melted","eqv":244,"eunit":"g","qty":1,"value":"90.33"},{"label":"cup, shredded","eqv":113,"eunit":"g","qty":1,"value":"41.83"},{"label":"oz","eqv":28.35,"eunit":"g","qty":1,"value":"10.50"},{"label":"cubic inch","eqv":17,"eunit":"g","qty":1,"value":"6.29"},{"label":"slice (1 oz)","eqv":28,"eunit":"g","qty":1,"value":"10.37"}]},{"nutrient_id":"208","name":"Energy","derivation":"NC","group":"Proximates","unit":"kcal","value":"404","measures":[{"label":"cup, diced","eqv":132,"eunit":"g","qty":1,"value":"533"},{"label":"cup, melted","eqv":244,"eunit":"g","qty":1,"value":"986"},{"label":"cup, shredded","eqv":113,"eunit":"g","qty":1,"value":"457"},{"label":"oz","eqv":28.35,"eunit":"g","qty":1,"value":"115"},{"label":"cubic inch","eqv":17,"eunit":"g","qty":1,"value":"69"},{"label":"slice (1 oz)","eqv":28,"eunit":"g","qty":1,"value":"113"}]},{"nutrient_id":"203","name":"Protein","derivation":"NONE","group":"Proximates","unit":"g","value":"22.87","measures":[{"label":"cup, diced","eqv":132,"eunit":"g","qty":1,"value":"30.19"},{"label":"cup, melted","eqv":244,"eunit":"g","qty":1,"value":"55.80"},{"label":"cup, shredded","eqv":113,"eunit":"g","qty":1,"value":"25.84"},{"label":"oz","eqv":28.35,"eunit":"g","qty":1,"value":"6.48"},{"label":"cubic inch","eqv":17,"eunit":"g","qty":1,"value":"3.89"},{"label":"slice (1 oz)","eqv":28,"eunit":"g","qty":1,"value":"6.40"}]},{"nutrient_id":"204","name":"Total lipid (fat)","derivation":"NONE","group":"Proximates","unit":"g","value":"33.31","measures":[{"label":"cup, diced","eqv":132,"eunit":"g","qty":1,"value":"43.97"},{"label":"cup, melted","eqv":244,"eunit":"g","qty":1,"value":"81.28"},{"label":"cup, shredded","eqv":113,"eunit":"g","qty":1,"value":"37.64"},{"label":"oz","eqv":28.35,"eunit":"g","qty":1,"value":"9.44"},{"label":"cubic inch","eqv":17,"eunit":"g","qty":1,"value":"5.66"},{"label":"slice (1 oz)","eqv":28,"eunit":"g","qty":1,"value":"9.33"}]},{"nutrient_id":"205","name":"Carbohydrate, by difference","derivation":"NC","group":"Proximates","unit":"g","value":"3.09","measures":[{"label":"cup, diced","eqv":132,"eunit":"g","qty":1,"value":"4.08"},{"label":"cup, melted","eqv":244,"eunit":"g","qty":1,"value":"7.54"},{"label":"cup, shredded","eqv":113,"eunit":"g","qty":1,"value":"3.49"},{"label":"oz","eqv":28.35,"eunit":"g","qty":1,"value":"0.88"},{"label":"cubic inch","eqv":17,"eunit":"g","qty":1,"value":"0.53"},{"label":"slice (1 oz)","eqv":28,"eunit":"g","qty":1,"value":"0.87"}]},{"nutrient_id":"291","name":"Fiber, total dietary","derivation":"NONE","group":"Proximates","unit":"g","value":"0.0","measures":[{"label":"cup, diced","eqv":132,"eunit":"g","qty":1,"value":"0.0"},{"label":"cup, melted","eqv":244,"eunit":"g","qty":1,"value":"0.0"},{"label":"cup, shredded","eqv":113,"eunit":"g","qty":1,"value":"0.0"},{"label":"oz","eqv":28.35,"eunit":"g","qty":1,"value":"0.0"},{"label":"cubic inch","eqv":17,"eunit":"g","qty":1,"value":"0.0"},{"label":"slice (1 oz)","eqv":28,"eunit":"g","qty":1,"value":"0.0"}]},{"nutrient_id":"269","name":"Sugars, total","derivation":"AS","group":"Proximates","unit":"g","value":"0.48","measures":[{"label":"cup, diced","eqv":132,"eunit":"g","qty":1,"value":"0.63"},{"label":"cup, melted","eqv":244,"eunit":"g","qty":1,"value":"1.17"},{"label":"cup, shredded","eqv":113,"eunit":"g","qty":1,"value":"0.54"},{"label":"oz","eqv":28.35,"eunit":"g","qty":1,"value":"0.14"},{"label":"cubic inch","eqv":17,"eunit":"g","qty":1,"value":"0.08"},{"label":"slice (1 oz)","eqv":28,"eunit":"g","qty":1,"value":"0.13"}]},{"nutrient_id":"301","name":"Calcium, Ca","derivation":"NONE","group":"Minerals","unit":"mg","value":"710","measures":[{"label":"cup, diced","eqv":132,"eunit":"g","q
at org.json.JSON.typeMismatch(JSON.java:111)
at org.json.JSONArray.<init>(JSONArray.java:96)
at org.json.JSONArray.<init>(JSONArray.java:108)
at com.monreal.deb.macrocalculator.fetchData.doInBackground(fetchData.java:38)
at com.monreal.deb.macrocalculator.fetchData.doInBackground(fetchData.java:17)
at android.os.AsyncTask$2.call(AsyncTask.java:333)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:245)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
I am extremely grateful for any help that you can provide.
Use Gson for parsing JSON. Its simpler and easier to implement. You just have to define some classes which will contain the parsed information from your JSON. From your JSON structure, you need to have the following classes.
Report.java
public class Report {
public Food food;
public String sr;
public String type;
}
Then Food.java
public class Food {
public Nutrients[] nutrients;
public String manu;
public String name;
public String ndbno;
public String ru;
public String ds;
}
Nutrients.java
public class Nutrients {
public String unit;
public String derivation;
public String name;
public String nutrient_id;
public String value;
public Measures[] measures;
public String group;
}
And finally, Measures.java
public class Measures {
public String eqv;
public String eunit;
public String value;
public String qty;
public String label;
}
Once you have these classes in your project, you are now ready to parse the JSON data using Gson with the following.
Gson gson = new Gson();
Report report = gson.fromJson(yourJsonString, Report.class);
To use Gson, you need to add the following dependency, in your build.gradle file.
dependencies {
compile 'com.google.code.gson:gson:2.8.2'
}
Hope that helps!
Related
error while Deserialize the Google Direction json
I have this json https://maps.googleapis.com/maps/api/directions/json?origin=-22.8895625,-47.0714089&destination=-22.892376,-47.027553&key= And I need deserialize it But I get this error Additional text encountered after finished reading JSON content: ,. Path '', line 8, position 4. What I am doing: public static async Task<List<Model.Localizacao>> GetDirectionsAsync(Localizacao locUser, Localizacao locLoja) { using (var client = new HttpClient()) { try { List<Model.Localizacao> lstLoc = new List<Model.Localizacao>(); var json = await client.GetStringAsync("https://maps.googleapis.com/maps/api/directions/json?origin=" + locUser.latitude + "," + locUser.longitude + "&destination="+ locLoja.latitude+","+locLoja.longitude+"&key=" + GOOGLEMAPSKEY); json = json.Substring(json.IndexOf('[')); json = json.Substring(0, json.LastIndexOf(']') + 1); lstLoc = JsonConvert.DeserializeObject<List<Model.Localizacao>>(json); return lstLoc; } catch (Exception ex) { Debug.WriteLine(ex.Message); return null; } } } this is my class: namespace neoFly_Montana.Model { class Localizacao { public double latitude { get; set; } public double longitude { get; set; } } } How can I solve that? My key is the same for the maps of google
I believe the problem is in these lines: json = json.Substring(json.IndexOf('[')); json = json.Substring(0, json.LastIndexOf(']') + 1); This appears to set json to be all the text from the first [ to the last ]. That means that you're going to wind up with some malformed json. geocoded_waypoints is an array, but so is routes, which means you're going to wind up with a String that looks like this: [ { "geocoder_status" : "OK" ... } { "geocoder_status" : "OK" ... } ], "routes": [ { "bounds": { ... } ... } ] That , "routes": [ will fail to parse. Update After some discussion in the comments, I think I'm at the end of the help I'm able to provide. I'm not familiar with C# or the particular JSON parsing library you're using. However, I can offer some ideas as a starting point. The JSON coming back from that Google call has a particular structure. I suspect you will have to create new model classes that match this structure. For example, the top-level object would have three fields, and might look like this in Java: public class ApiResponse { private List<Waypoint> geocoded_waypoints; private List<Route> routes; private String status; } Then you'd have to implement Waypoint and Route, again matching the structure of the Google response: public class Waypoint { private String geocoder_status; private String place_id; private List<String> types; } public class Route { private Bounds bounds; private String copyrights; private List<Leg> legs; private Polyline overview_polyline; private String summary; private List<String> warnings; private List<String> waypoint_order; } And so on. Once you have a class to represent the top-level response as well as all the various sub-objects inside that response, you would probably be able to change this code: List<Model.Localizacao> lstLoc = new List<Model.Localizacao>(); var json = await client.GetStringAsync("https://maps.googleapis.com/maps/api/directions/json?origin=" + locUser.latitude + "," + locUser.longitude + "&destination="+ locLoja.latitude+","+locLoja.longitude+"&key=" + GOOGLEMAPSKEY); json = json.Substring(json.IndexOf('[')); json = json.Substring(0, json.LastIndexOf(']') + 1); lstLoc = JsonConvert.DeserializeObject<List<Model.Localizacao>>(json); return lstLoc; to this: var json = await client.GetStringAsync("https://maps.googleapis.com/maps/api/directions/json?origin=" + locUser.latitude + "," + locUser.longitude + "&destination="+ locLoja.latitude+","+locLoja.longitude+"&key=" + GOOGLEMAPSKEY); return JsonConvert.DeserializeObject<ApiResponse>(json);
From JSon array to custom data structure
Say I need to fill a Binary Search Tree with data obtained from Server. Say further that the data coming from server is a json array of nodes "parts":[{"id":1,"name":"apple"},{"id":12,"name":"orange"},{"id":21,"name":"pen"},{"id":214,"name":"kite"}]//where each {} represents a node How do I use GSon to read the array of Nodes into my BST? If you recall a BST has two classes public class BST{ private Note root; } public class Node{ String el; Node left, right; } If BST is too hard, image something simpler public class MyDataStructure{ private List<Part> partsList; … } public class Part{ String el; List<String> stuff; } How do I populate MyDataStructure with partsList using GSon on android? As a side note, I would rather help solving the MyDataStruction version of the problem.
ok.. you can use this as reference: define a class pojo and a Fruit(is the array/list/collection) the pojo class Pojo { #Override public String toString() { return "Pojo [parts=" + parts + "]"; } private List<Fruits> parts; } the fruit class Fruits { private int id; private String name; #Override public String toString() { return "[id=" + id + ", name=" + name + "]"; } } the implementation String json = "{\"parts\": [{\"id\":1,\"name\":\"apple\"},{\"id\":2,\"name\":\"pear\"},{\"id\":3,\"name\":\"kiwi\"}]}"; Gson g = new Gson(); Pojo p = g.fromJson(json, Pojo.class); System.out.println(p); the MyDataStructure population add to the pojo a setter getter so you can work with the list, add too setter and getter for the fruit class so you can get the id and the name.. so in the pojo object p you can do p.getList() and iterate over the elements Something like: Pojo p = g.fromJson(json, Pojo.class); System.out.println(p); for (Fruits f : p.getParts()) { System.out.println(f.getId()); System.out.println(f.getName()); }
use JsonDeserializer on GSON
I have a model that work with multiple JSON responses. However this a response : items: [ { kind: "youtube#playlistItem", etag: ""fpJ9onbY0Rl_LqYLG6rOCJ9h9N8/jqbcTLu8tYm8b1FXGO14gNZrFG4"", id: "PLUQ7I1jJqKB4lJarGcpWsP62l7iC06IkE2LDE0BxLe18", That conflict with this one (notice the same id field with different type. The above id is String, the other is a Class) : items: [ { kind: "youtube#searchResult", etag: ""fpJ9onbY0Rl_LqYLG6rOCJ9h9N8/hDIU49vmD5aPhKUN5Yz9gtljG9A"", id: { kind: "youtube#playlist", playlistId: "PLh6xqIvLQJSf3ynKVEc1axUb1dQwvGWfO" }, I want to read the id field using a single model class. This is my model Response class : public class Response { private ArrayList<Item> items = new ArrayList<Item>(); And this is my model Item class : public class Item { private Id id; //nested class inside Item public class Id { private String id; private String kind; private String playlistId; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getKind() { return kind; } public void setKind(String kind) { this.kind = kind; } public String getPlaylistId() { return playlistId; } public void setPlaylistId(String playlistId) { this.playlistId = playlistId; } } Notice that the Id class is inside Item class. This is how i use registerTypeAdapter : GsonBuilder gsonBuilder = new GsonBuilder(); gsonBuilder.registerTypeAdapter(Response.class, new JsonDeserializer<Item.Id>() { #Override public Item.Id deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException { Item.Id result = new Item().new Id(); if(jsonElement.isJsonPrimitive() == false) { result.setKind(jsonElement.getAsJsonObject().get("kind").getAsString()); result.setPlaylistId(jsonElement.getAsJsonObject().get("playlistId").getAsString()); //return new Item.Id(jsonElement.getAsJsonObject().get("kind").getAsString(), jsonElement.getAsJsonObject().get("playlistId").getAsString()); return result; } else { result.setId(jsonElement.getAsString()); //return new Item.Id(jsonElement.getAsString()); return result; } } }); Gson gson = gsonBuilder.create(); Response result = Response.success( gson.fromJson(json, gsonClass), HttpHeaderParser.parseCacheHeaders(response)); However the above code throws java.lang.NullPointerException in this line : if(jsonElement.isJsonPrimitive() == false) What should i do? Is this the correct way to use registerTypeAdapter? Thanks for your time
Your main problem seems to be that you're registering a type adapter for Response class but you're giving a JsonDeserializer that handles Item.Id classes. Either you deserialize a full Response or limit yourself to Item or even Item.Id. Another problem with deserializing Item.Id (as written in your question) directly is that it's a non-static inner class, which requires an instance of the parent class to be instantiated (as you do with the new Item().new Id()). I think that you would have to deserialize the whole Item manually if you want to keep it as such, frankly I don't see any reason not to make Item.Id static as it would simplify the problem. Here's my solution with the static version of Item.Id GsonBuilder builder = new GsonBuilder(); builder.registerTypeAdapter(Item.Id.class, new JsonDeserializer<Item.Id>() { #Override public Item.Id deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { Item.Id id = new Item.Id(); if (!json.isJsonPrimitive()) { JsonObject jsonObject = json.getAsJsonObject(); id.setKind(jsonObject.get("kind").getAsString()); id.setPlaylistId(jsonObject.get("playlistId").getAsString()); } else { id.setId(json.getAsString()); } return id; } }); And some test snippets that seemed to work for me: Gson gson = builder.create(); Response response = gson.fromJson("{\n" + " \"items\": [\n" + " {\n" + " \"kind\": \"youtube#playlistItem\",\n" + " \"etag\": \"fpJ9onbY0Rl_LqYLG6rOCJ9h9N8/jqbcTLu8tYm8b1FXGO14gNZrFG4\",\n" + " \"id\": \"PLUQ7I1jJqKB4lJarGcpWsP62l7iC06IkE2LDE0BxLe18\"\n" + " }\n" + " ]\n" + "}", Response.class); Response response2 = gson.fromJson("{\n" + " \"items\": [\n" + " {\n" + " \"kind\": \"youtube#searchResult\",\n" + " \"etag\": \"fpJ9onbY0Rl_LqYLG6rOCJ9h9N8/hDIU49vmD5aPhKUN5Yz9gtljG9A\",\n" + " \"id\": {\n" + " \"kind\": \"youtube#playlist\",\n" + " \"playlistId\": \"PLh6xqIvLQJSf3ynKVEc1axUb1dQwvGWfO\"\n" + " }\n" + " }\n" + " ]\n" + "}", Response.class); If you want to keep Item.Id non-static, I think you need to write a deserializer for Item instead. Also keep in mind that json elements can be absent but the parser should still be able to handle it.
I had just the same problem long time ago, but i was working then with Json.Net, However json concept is (almost) the same so I hope I will succeed help. I guess it might not work, but why try to not register with the Item class? I mean there is no 'Item.Id' in so it's obious get a null. BUT this: gsonBuilder.registerTypeAdapter(Item.class, likely to fail, becaus you dont parse the Item class explicitly (However it might work, I just never tried it in Gson. But in Json.Net similar way would work proper.) So, how could you fix that? I'm too lazy to write an paraser but you could look here and read the surt summary summary from here (Search for Using the registerTypeAdapter(). I hope I helped.
JSON parsing using GSON - Setting up class hierarchy
I can't seem to wrap my head around how to setup my class hierarchy for JSON conversion using GSON. My JSON looks like: { "Users": { "id": 1, "name": "Jim", "location": "Huntsville" } } My User List class looks like: public class UserList { public static List<User> Users; #SuppressWarnings("static-access") public void setUserList(List<User> userList){ this.Users = userList; } public List<User> getUserList(){ return Users; } } and lastly a user class that looks like this: public class User { private int id; private String name; private String location; public int getId(){ return id; } public String getName(){ return name; } public String getLocation(){ return location; } public String toString(){ return("User: [id=" + id + "], [name=" + name + "], [location=" + location + "]"); } } Anyone mind giving me a shove in the right direction? I'd appreciate it! EDIT: Forgot to show my parsing code.. (Just reading a sample JSON file from SDCard) BufferedReader br = new BufferedReader(new FileReader(Environment.getExternalStorageDirectory() + "/user.json")); UserList userList = gson.fromJson(br, UserList.class);
are you sure your example JSON is correct? It does not seem to be a list of things, just one user is defined. Furthermore, your getter and setters for Users, should be following the get/set pattern and be called public List<User> getUsers() public void setUsers(List<User> users) Also, you can follow the Java convention of small case and instruct Gson to use a different casing. Assuming that you only have one entry of Users in your JSON. This would let you parse the snippit you provided, if you change the Users property into User not a list. #SerializedName("Users") private User user; So if you want a list of users you should find that in the json, this should let you parse it as a list, !note that you need to have objects, which are enclosed, like: {"users" : [{id:"one"}, ...]} As pointed out in the comments.
Using GSON in Android to parse a complex JSON object
I'm relatively new to Java programming and need to parse a complex JSON object across the wire. I've been reading documentation on GSON the past day and Haven't had much luck being able to fully parse this type of structure: { 'Events' : [{ 'name' : 'exp', 'date' : '10-10-2010', 'tags' : ["tag 1", "tag2", "tag3"] },...more events...], 'Contacts' : [{ 'name' : 'John Smith', 'date' : '10-10-2010', 'tags' : ["tag 1", "tag2", "tag3"] },...more contacts...], } I've been able to get it to work similarly to this question but can't figure out how to get that additional array level to work.
The correct way to do it using GSON in the format I'm looking for is: //somewhere after the web response: Gson gson = new Gson(); Event[] events = gson.fromJson(webServiceResponse, Event[].class); //somewhere nested in the class: static class Event{ String name; String date; public String getName() { return name; } public String getDate() { return date; } public void setName(String name) { this.name = name; } public void setDate(String date) { this.date = date; } }
Java has his own JSON parser: we can use it on Android as well. Below you can find how you can get all events from your String. String TAG = "JSON EXAMPLE"; String jsonString = "{\"Events\" : [{\"name\" : \"exp\",\"date\" : \"10-10-2010\",\"tags\" : [\"tag 1\",\"tag 2\",\"tag 3\"]}],\"Contacts\" : [{\"name\" : \"John Smith\",\"date\" : \"10-10-2010\",\"tags\" : [\"tag 1\",\"tag 2\",\"tag 3\"]}]}"; try { JSONObject jsonObj = new JSONObject(jsonString); // create a json object from a string JSONArray jsonEvents = jsonObj.getJSONArray("Events"); // get all events as json objects from Events array for(int i = 0; i < jsonEvents.length(); i++){ JSONObject event = jsonEvents.getJSONObject(i); // create a single event jsonObject Log.e(TAG, "Event name:" + event.getString("name") + " date: " + event.getString("date")); JSONArray eventTags = event.getJSONArray("tags"); for(int j = 0; j < eventTags.length(); j++){ Log.e(TAG, "Event tag: " + eventTags.getString(j)); } } } catch (JSONException e) { e.printStackTrace(); } Be aware: Your JSON Object(from your question) will throw a exception because it is not valid( I'm not sure, but it look like a javascript object). You have to add some quotes to each property(key) and ecape them with \ (\"). This tool is really nice to test if a JSON String is valid or not.