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,
\
I am also face the same problem. I found that, issue was every if condition I am getting the jsonReader.nextName(). It is not correct way. I think your also doing the same way.
From your code these two lines create the problem.
// 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();
we should use "jsonReader.nextName()" one time only for each iteration in while loop like
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();
}
}
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.
If you're going to use JsonReader
and parse the stream manually, you don't use the automatic deserialization via Gson.fromJson()
.
You either need to extract the fields from each object while traversing that array once you're in your loop, or simply use the automatic deserialization with an appropriate class (which, honestly, is what you should be doing):
class Response {
private int total;
private String jsonrpc;
private int id;
private List<Country> result;
// getters and setters ...
}
And then simply:
InputStreamReader isr = new InputStreamReader(response.getEntity()
.getContent(), "UTF-8"));
Response r = gson.fromJson(isr, Response.class);