0

I've been looking for example Gson streaming API code, and I see a lot of this style:

       reader.beginObject();
       while (reader.hasNext()) {

          switch(reader.peek()) {
             case NAME:
                ...
             case BEGIN_ARRAY:
                ...
             case END_ARRAY:
                ...

                }

       reader.endObject();
       reader.close();

That code works great when the JSON has only one "top-level" object. The hasNext() loop terminates at the end of that first object. What I need is a loop around that to process ALL the objects. Here's an abbreviated example of my JSON:

{
  "meta": {
    "disclaimer": "Loren Ipsum",
    "terms": "https://myURL/terms/",
    "license": "https://myURL/license/",
    "last_updated": "2023-03-10",
    "totals": {
      "skip": 0,
      "limit": 12000,
      "total": 362818
    }
  },
  "results": [
    {"result": "value1"},
    {"result": "value2"},
    {"result": "value3"},
  ]
}

This code processes the "meta" object just fine, and never get to the "results" array.

Searched high and low.

NoazDad
  • 608
  • 3
  • 9

1 Answers1

0

Normally you never have to handle NAME and END_ARRAY when using peek():

  • JSON specifies that objects consist of pairs of member names and values. So you only need a call hasNext() in a loop, and while the result is true call nextName(), followed by a value reading method such as nextInt() (possibly after checking the type with peek() first).
  • If the loop condition is hasNext() then you won't encounter an END_ARRAY inside the loop.

The general structure for reading JSON arrays and objects from a JsonReader is:

jsonReader.begin###();
while (jsonReader.hasNext()) {
    ... // read values
}
jsonReader.end###();

And you can arbitrarily nest this. Here is an example for this, based on your JSON data. The main point of this example is to demonstrate the nesting, the code is not very useful and also quite verbose:

jsonReader.beginObject();
while (jsonReader.hasNext()) {
    switch (jsonReader.nextName()) {
        case "meta": {
            jsonReader.beginObject();
            while (jsonReader.hasNext()) {
                String name = jsonReader.nextName();
                switch (jsonReader.peek()) {
                    case STRING:
                        System.out.println(name + ": " + jsonReader.nextString());
                        break;
                    default:
                        jsonReader.skipValue();
                        System.out.println(name + ": <skipped>");
                        break;
                }
            }
            jsonReader.endObject();
            break;
        }
        case "results": {
            jsonReader.beginArray();
            while (jsonReader.hasNext()) {
                jsonReader.beginObject();
                while (jsonReader.hasNext()) {
                    System.out.println(jsonReader.nextName() + ": " + jsonReader.nextString());
                }
                jsonReader.endObject();
            }
            jsonReader.endArray();
            break;
        }
        default:
            throw new IllegalArgumentException("Unsupported name");
    }
}
jsonReader.endObject();

But is it really necessary in your use case to directly read from a JsonReader? If not, then it would be probably less error-prone to just create model classes matching your JSON data (e.g. class Meta { String disclaimer; URL terms; ... }) and use one of the Gson.fromJson methods with a Reader as parameter. Internally that will also parse JSON in a streaming way, so the only overhead is that afterwards the complete deserialized object exists in memory. But maybe that is exactly what you need.

Marcono1234
  • 5,856
  • 1
  • 25
  • 43
  • 1. I don't have standard JSON. This is intended to process any JSON input. One of the arguments to my function is the name of the array node to be returned as a table. 2. I tried using ```Gson.fromJson()``` on the entire doc, but it blew out the Java heap. The file is over 3GB. What I want to do: fast-forward through the doc until I hit the desired JSON element name - AND the next token is a START_ARRAY. THEN, pop each array element as a separate JSON doc onto a and return when I hit the END_ARRAY. In this case, I'm looking for the "results" array - but I never get there. – NoazDad Mar 17 '23 at 21:12
  • @NoazDad, please include this information in your question then, and ideally also your complete code (or minimized to what is causing the problem, but still being runnable) so that it is easier to provide a specific answer to that. – Marcono1234 Mar 17 '23 at 23:04