1

Let's say I have a file sample.json which contains an array of objects as shown below.

[ {Name: "ABC", "email" : "[email protected]", "city" : "NewYork"}, {Name: "XYZ", "email" : "[email protected]", "city" : "NewJersey"}]

I am using TestNG framework and GSON library to parse JSON.

Below is the class where I want to parse JSON object wise. My aim is to filter the object by its name in array and return only that object. I am using Streaming API to avoid returnig all the data at once present in json file.

public class JSON{

private JsonObject jsonObject;

@Test
public void readJSON(){
    getJSONObject("C:\\..\\sample.json", "ABC");
    readData("email");
}

public JsonObject getJSONObject(fileName, Name){
JsonReader jsonReader = new JsonReader(new FileReader(fileName));
jsonReader.beginArray();
while (jsonReader.hasNext()){
    str = jsonReader.nextString();
    If (str.equals(Name)){
            System.out.println("Found Name");
            // To get the object - we can use fromJson(jsonReader, Person.class),
            // where Person.class defines all the json variables as class variables.        
            // But I want to use JsonParser or something like that to return just this object which has the Name "ABC"

        }
    }
} }

Reason why I don't want to create a Person class and use fromJSON because I use this function for different types of JSON strings. So I don't prefer to create different classes for different strings. I want to use something else instead which just returns the required object. Also I am not sure how to iterate through the array using hasNext() of Streaming API method to find the required object.

Any inputs are appreciated. Thank you.

1

2 Answers 2

2

3rd Party Libraries

If you don't have resource/performance issues loading the entire Json document I would recommend JsonPath to give you Xpath-like access to pick out just the matches you want.

However, I assume that you want to stream the Json data because it is causing you real problems that you have actually measured/tested. There is some talk around having a streaming extension for JsonPath however I have no experience with it.

That got me thinking about other alternatives you could use and I came across the stoppable parser in JsonSurfer which looks like it may solve your problem.

Roll your own

I started writing you a piece of code using your original question but I found myself having to create each Person class as I streamed the Json, which seemed to be what you are trying to avoid. Let me know if you want to persevere with a "roll your own" approach rather than the 3rd party libraries I mentioned.

I used this post Gson streaming tutorial to help me understand how to parse the Json tokens while streaming.

Sign up to request clarification or add additional context in comments.

1 Comment

JsonPath is very convenient if performance is not under consideration. Thank you for letting me know.
1

I don't recommend the streaming API, it's really quite painful to use. But in your case where you want to extract just one object out of an array it is possible.

    JsonReader reader = new JsonReader(new StringReader(
            "[ {Name: \"ABC\", \"email\" : \"[email protected]\", \"city\" : \"NewYork\"}, {Name: \"XYZ\", \"email\" : \"[email protected]\", \"city\" : \"NewJersey\"}]\n"));


    String target = "ABC";

    ArrayList<String> names = new ArrayList<String>();

    reader.setLenient(true);
    reader.beginArray();
    while(reader.hasNext()) {
        reader.beginObject();
        // peek is your best friend in this world, as it tells you what object you're looking at
        while(reader.peek() != JsonToken.END_OBJECT) {
            if(reader.peek() == JsonToken.NAME) {
                String name = reader.nextName();
                if(name.equals("Name")) {
                    if(reader.nextString().equals(target)) {
                        // I'm just printing these out right now but you could construct an object and set variables instead.
                        while(reader.peek() == JsonToken.NAME) {
                            switch (reader.nextName()) {
                                case "email":
                                    System.out.println("Found email for " + target + " " + reader.nextString());
                                    break;
                                case "city":
                                    System.out.println("Found city for " + target + " " + reader.nextString());
                                    break;

                            }
                        }
                    }

                }
            }
            //this is really the key to the parser which is to skip all the values that you're not interested in
            reader.skipValue();
        }


        reader.endObject();
    }
    reader.endArray();
}

5 Comments

Thank you for your answer. I am new to Java & Json. The only reason why I would like to use Streaming API is that json file might end up having large sets of testdata and I might need only few rows among them for current execution. So I don't want to retreive all of them at once. My aim is to get the required object and assign it to a public variable of type JsonObject in my class and use this object to retreive values (email, city) in another function using this type of syntax - JsonObject.get("email").toString. So Is it possible to return only that object from your function?
Just return from the email case, that will return what you want an avoid scanning anything that you don't need. Also you can avoid the switch and just use if("email".equals(reader.nextName()) { return reader.nextString(); }
If I want to get email in one function and city in another function, I have to iterate through reader and verify it matches the Name = "ABC" both the times, right. What I thought is to return the element in array as JsonObject by iterating one single time and retrieve values using get method on it later. Looks like we can't return just that object from this function and Streaming API has been implemented to iterate token by token.
May be I can create a dictionary when the match is found for first time and use it later to retreive values.
That's what I would do, create a map and read all the entries and pass that back to the caller. The overhead would be minimal

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.