GSON бросает "Ожидаемое Ожидаемое имя, но было NUMBER в строке 1 столбца 8"?

Я пытаюсь проанализировать строку JSON, подобную этой (сгенерированный URL-адрес с 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"
    }
  ]

}

Здесь класс объектов, который я использую:

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;
}




} 

Но он меня бросает:

     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)

в Json "id": 1, это число! вот в чем причина? Я генерирую JSON с http://www.json-generator.com

Мой код Aysc для анализа 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();
            }
        }

    }

}

Любые идеи, как это исправить?

Спасибо!

Ответ 1

Проблема в вашем коде заключалась в том, что вы зацикливаете на именах (json keys):

while (jsonReader.hasNext()) {

вы получите следующее имя:

String name = jsonReader.nextName();

но затем вы потребляете значение только в том случае, если это имя равно result.

Вспомните JsonReader как способ перемещения курсора вперед в json: чтение его токена с помощью токена. Каждая команда, которую вы ему даете, либо читает то, на что указывает курсор, либо перемещает курсор, иногда вы можете перемещать курсор и читать значение с помощью одной команды (например, с помощью nextName()).

Если имя не result, JsonReader останется в том месте, где вы его оставили: имя или ключ, которые вы только что прочитали, с помощью nextName().

Таким образом, в следующий раз, когда он вызывает nextName(), курсор переместится от ключа к значению (next), и он ожидает найти name, и вместо этого он найдет значение предыдущего имени, в вашем случае NUMBER потому что первое значение в вашем json - это число (имя: "total", значение: 56).

если вы не читаете значение, потому что вас это не волнует, вы должны позвонить

jsonReader.skipValue();

означает, что вы должны изменить свой код в

if (name.equals("result")) {
  // read it
} else {
  jsonReader.skipValue();
}

Сказанное: вы должны либо использовать аннотацию, либо, если по какой-то причине вы не можете зарегистрировать один или несколько TypeAdapter для задания анализа ваших пользовательских объектов.

Код внутри этого адаптера очень похож на тот, который вы написали, но он ограничен одной частью json, которая соответствует вашему объекту.

Так, например, если вы создаете класс CountriesResult, содержащий ваши total и result типа List<Country>

public class CountriesResult {
  private int total;
  @SerializedName("result")
  private List<Country> countries;
}

У вас уже есть класс для страны с полями, которые вам нужны.

Затем вы попросите, чтобы GSon проанализировал ваш объект CountriesResult, он автоматически проанализирует его.

gson.fromJson(jsonString, CountriesResult.class);

Если вам нужно разобрать Country по-своему, вам просто нужно TypeAdatper для Country, см. этот пример. И зарегистрировать адаптер типа или использовать @JsonAdapter annotation.

Ответ 2

Если вы собираетесь использовать JsonReader и проанализировать поток вручную, вы не используете автоматическую десериализацию через Gson.fromJson().

Вам нужно либо извлекать поля из каждого объекта при обходе этого массива, когда вы находитесь в своем цикле, либо просто использовать автоматическую десериализацию с соответствующим классом (что, честно говоря, это то, что вы должны делать):

class Response {
    private int total;
    private String jsonrpc;
    private int id;
    private List<Country> result;

   // getters and setters ...
}

А потом просто:

InputStreamReader isr = new InputStreamReader(response.getEntity()
                                .getContent(), "UTF-8"));
Response r = gson.fromJson(isr, Response.class);

Ответ 3

Я тоже сталкиваюсь с той же проблемой. Я обнаружил, что проблема была в каждом условии, когда я получаю jsonReader.nextName(). Это неверный путь. Я думаю, что вы тоже делаете то же самое.

Из вашего кода эти две строки создают проблему.

  // here the jsonReader moving one step.
  Log.d("nextName", jsonReader.nextName());

  //again your trying to getting the "jsonReader.nextName()" it is wrong in the above line already you got the name and reader move to next line. So  Exception may be comes here.                                    
   String name = jsonReader.nextName();   

мы должны использовать "jsonReader.nextName()" один раз только для каждой итерации во время цикла, например,

 while (reader.hasNext())
 {

            String name=reader.nextName();
            if(name.equals(Constants.AUTH)){
                authDetails.setAuth(reader.nextString());

            }else if(name.equals(Constants.STATUS)){
                authDetails.setStatus(reader.nextInt());
            }else if(name.equals(Constants.MESSAGE)){
                authDetails.setMessage(reader.nextString());
            }else {
                reader.skipValue();
            }

    }