GSON throwing “Expected Expected a name but was NUMBER at line 1 column 8”?
android, gson, parsing
Solution
The problem in your code was that you loop on names (json keys):
while (jsonReader.hasNext()) {
you get the next name:
String name = jsonReader.nextName();
but then you consume the value only if this name is equal to `result`.
Think of the `JsonReader` as a way to move a cursor forward in your json: reading it token by token. Every command you give to it either read what the cursor is pointing to or move the cursor, sometimes you can move the cursor and read the value with a single command (like with `nextName()`).
When the name is not `result` the `jsonReader` stay in the position where you left it: the name or key you just read with `nextName()`.
So the next time it calls `nextName()` the cursor move from the key to the value (`next`) and it expect to find a `name` and instead it found the value of the previous name, in your case `NUMBER` because the first value in your json is a number (name: `"total"`, value: `56`).
if you are not reading the value because you don't care about it you should call
jsonReader.skipValue();
meaning you should change your code in
if (name.equals("result")) {
// read it
} else {
jsonReader.skipValue();
}
That said: you should either use annotation or, if you can't for some reason, register one or more `TypeAdapter` for the job of parsing your custom objects.
The code inside those adapter is very similar to the one you wrote but it is limited to a single part of the json that match your object.
So for example, if you create a class `CountriesResult` containing your `total`, and `result` of type `List<Country>`
public class CountriesResult {
private int total;
@SerializedName("result")
private List<Country> countries;
}
You already have a class for Country with the fields you need.
Then you ask GSon to parse your `CountriesResult` object it will automatically parse it.
gson.fromJson(jsonString, CountriesResult.class);
If you need to parse `Country` in a custom way you just need a `TypeAdatper` for `Country`, see this example. And to register the type adapter or use the `@JsonAdapter` annotation.
Problem
I'm trying to parse a JSON string like this one ( generated URL with http://www.json-generator.com) ``` { "total": 86, "jsonrpc": "2.0", "id": 1, "result": [ { "startDate": "14/03/2012", "meetingId": "1330", "creator": "Jhon", "lastModified": "02/04/2012", "meetingTitle": "task clarification", "location": "Conf hall", "startTime": "02:00 PM", "createdDate": "14/03/2012", "owner": "Peter", "endTime": "02:30 PM" }, { "startDate": "20/03/2012", "meetingId": "1396", "creator": "Mr.Hobbs", "lastModified": "07/09/2012", "meetingTitle": "Design Work", "location": "South conf Room", "startTime": "03:30 PM", "createdDate": "19/03/2012", "owner": "Steve Jobs", "endTime": "04:30 PM" }, { "startDate": "22/03/2012", "meetingId": "1432", "creator": "Robin", "lastModified": "21/03/2012", "meetingTitle": "Do something new", "location": "NA", "startTime": "10:00 AM", "createdDate": "21/03/2012", "owner": "Mr.Bean", "endTime": "11:00 AM" } ] ``` } Here's an object class I'm using: ``` public class Country { String startDate; String meetingId; String creator; String lastModified; String meetingTitle; String location; String startTime; String createdDate; String owner; String endTime; public String getStartDate() { return startDate; } public void setStartDate(String startDate) { this.startDate = startDate; } public String getMeetingId() { return meetingId; } public void setMeetingId(String meetingId) { this.meetingId = meetingId; } public String getCreator() { return creator; } public void setCreator(String creator) { this.creator = creator; } public String getLastModified() { return lastModified; } public void setLastModified(String lastModified) { this.lastModified = lastModified; } public String getMeetingTitle() { return meetingTitle; } public void setMeetingTitle(String meetingTitle) { this.meetingTitle = meetingTitle; } public String getLocation() { return location; } public void setLocation(String location) { this.location = location; } public String getStartTime() { return startTime; } public void setStartTime(String startTime) { this.startTime = startTime; } public String getCreatedDate() { return createdDate; } public void setCreatedDate(String createdDate) { this.createdDate = createdDate; } public String getOwner() { return owner; } public void setOwner(String owner) { this.owner = owner; } public String getEndTime() { return endTime; } public void setEndTime(String endTime) { this.endTime = endTime; } } ``` But it throws me with: ``` W/JSONStreamReader(1153): java.lang.IllegalStateException: Expected a name but was NUMBER at line 1 column 8 08-09 01:21:37.629: W/JSONStreamReader(1153): java.lang.IllegalStateException: Expected a name but was NUMBER at line 1 column 8 08-09 01:21:37.629: W/JSONStreamReader(1153): at com.google.gson.stream.JsonReader.nextName(JsonReader.java:785) 08-09 01:21:37.629: W/JSONStreamReader(1153): at com.example.gsontest.MainActivity$MyAsyncTask.doInBackground(MainActivity.java:162) ``` in Json "id": 1, is the number! is that is the reason? I'm generating JSON with http://www.json-generator.com My Aysc Code for Parsing Json: ``` private class MyAsyncTask extends AsyncTask<String, Void, Void> { private static final int REGISTRATION_TIMEOUT = 3 * 1000; private static final int WAIT_TIMEOUT = 30 * 1000; private final HttpClient httpclient = new DefaultHttpClient(); final HttpParams params = httpclient.getParams(); private boolean error = false; protected Void doInBackground(String... urls) { String URL = null; Log.d("ConnManagerParams", "ok?"); try { // URL passed to the AsyncTask URL = urls[0]; HttpConnectionParams.setConnectionTimeout(params, REGISTRATION_TIMEOUT); HttpConnectionParams.setSoTimeout(params, WAIT_TIMEOUT); ConnManagerParams.setTimeout(params, WAIT_TIMEOUT); Log.d("ConnManagerParams", "ok?"); HttpPost httpPost = new HttpPost(URL); Log.d("httpPost", "ok?"); // Response from the Http Request HttpResponse response = httpclient.execute(httpPost); Log.d("response", "ok?"); // Check the Http Request for success StatusLine statusLine = response.getStatusLine(); Log.d("statusLine", response.getStatusLine().toString()); // Log.d("RESPONSE", // EntityUtils.toString(response.getEntity())); if (statusLine.getStatusCode() == HttpStatus.SC_OK) { // Log.d("statusok", statusLine.getStatusCode()); Gson gson = new Gson(); // create a new JSON reader from the response input stream Log.d("gson", "gson?"); JsonReader jsonReader = new JsonReader( new InputStreamReader(response.getEntity() .getContent(), "UTF-8")); // begin parsing Log.d("AFTjsonReader", "AFTjsonReader?"); jsonReader.beginObject(); Log.d("beginObject", "beginObject?"); // stay in loop as long as there are more data elements while (jsonReader.hasNext()) { // get the element name Log.d("whilejsonReader", "whilejsonReader?"); // String name = jsonReader.nextName(); Log.d("nextName", jsonReader.nextName()); String name = jsonReader.nextName(); Log.d("nextNametest2", "nextName?"); if (name.equals("result")) { Log.d("result", "result?"); jsonReader.beginArray(); while (jsonReader.hasNext()) { // parse every element and convert that to a // country object Country country = gson.fromJson(jsonReader, Country.class); // add the country object to the list countryList.add(country); } jsonReader.endArray(); } // success = jsonReader.nextBoolean(); success = true; } // end reader and close the stream jsonReader.endObject(); jsonReader.close(); } else { // Closes the connection. Log.d("Closes the connection.", "Closes the connection.?"); Log.w(LOG_TAG, statusLine.getReasonPhrase()); response.getEntity().getContent().close(); throw new IOException(statusLine.getReasonPhrase()); } } catch (Exception e) { Log.d("catch", "catch"); Log.w(LOG_TAG, e); error = true; cancel(true); } return null; } protected void onCancelled() { Log.e(LOG_TAG, "Error occured during data download"); } protected void onPostExecute(Void unused) { if (error) { Log.e(LOG_TAG, "Data download ended abnormally!"); } else { displayCountries(); } } } } ``` Any ideas how should I fix it? Thanks!