Parse JSON object recursively - Java

13,559

Solution 1

As mentioned in my comment.

Your first issue would be the content in your JSON file. Based on the standard, it should be wrapped around with a set of { }.

Example

{ 
    "members": [
        {
            "firstName": "Bruce",
            "familyMembers": null
        },
        {
            "firstName": "Gates Family",
            "familyMembers": [
                {
                    "firstName": "Bill",
                    "familyMembers": null
                },
                {
                    "firstName": "Steve",
                    "familyMembers": null
                }
            ]
        },
        {
            "firstName": "Lee",
            "familyMembers": null
        },
        {
            "firstName": "Chan",
            "familyMembers": null
        }
    ]
}

Also, I think the value "Gates Family" should be part of the output? Since it is under the "FirstName" attribute.

Anyway, here is my solution that is based on the org.json library. It also uses Goggle's GSon library where I use it for reading the JSON file.

import org.json.*;

import java.io.File;
import java.util.HashMap;
import java.util.Map;

import com.google.common.base.Charsets;
import com.google.common.io.Files;

public class solution {

    public static final String JSON_DATA_FILE_PATH = "./data/source_37848106.json";

    private static boolean hasMoreFamilyName(JSONObject json) {
        return json.has("familyMembers") && json.get("familyMembers") != JSONObject.NULL;
    }

    private static void trackFirstName(Map<String, Integer> nameTracker, JSONObject json) {
        if (!nameTracker.containsKey(json.getString("firstName"))) {
            nameTracker.put(json.getString("firstName"), /*DUMMY VALUE =*/1);
        }
    }

    private static void getNames(Map<String,Integer> nameTracker, JSONArray jsonArr) {
        for (int i = 0; i< jsonArr.length(); i++) {
            JSONObject item = jsonArr.getJSONObject(i);
            if (hasMoreFamilyName(item)) {
                getNames(nameTracker, item.getJSONArray("familyMembers"));
            }
            trackFirstName(nameTracker, item);
        }
    }

    public static void main(String[] args) {
        Map<String, Integer> nameTracker = new HashMap<>();

        try {
            String text = Files.toString(new File(JSON_DATA_FILE_PATH), Charsets.UTF_8);
            JSONObject json = new JSONObject(text);
            getNames(nameTracker, json.getJSONArray("members"));
        }
        catch (Exception ex) {
            System.out.println("Something is wrong.");
        }

        for (Map.Entry<String,Integer> entry : nameTracker.entrySet()) {
        System.out.println(entry.getKey());
    }
}

Solution 2

Try my recursive implementation.

public static void jsonArrayToSet(JSONArray jAry, Set<String> result, String targetKey, String subArrayKey, boolean includeNode){
    try {
        for (int i = 0; i < jAry.length(); i++) {
            JSONObject jObj = jAry.getJSONObject(i);
            boolean hasSubArray = false;
            JSONArray subArray = null;
            if(jObj.has(subArrayKey)){
                Object possibleSubArray = jObj.get(subArrayKey);
                if(possibleSubArray instanceof JSONArray){
                    hasSubArray = true;
                    subArray = (JSONArray) possibleSubArray;
                }
            }
            if(hasSubArray){
                if(includeNode){
                    result.add(jObj.getString(targetKey));
                }
                jsonArrayToSet(subArray, result, targetKey, subArrayKey, includeNode);
            } else {
                result.add(jObj.getString(targetKey));
            }
        }
    } catch (JSONException e){
        e.printStackTrace();
    }
}

jAry: The source JSONArray.

result: The Set you want to write in.

targetKey: The key that maps to an entry which you want to add to result.

subArrayKey: The key that map to a sub-JSONArray.

includeNode: When current JSONOnject is a node containing sub-array, add it to result or not.

In your case, you can call:

jsonArrayToSet(yourJsonArray, yourSet, "firstName", "familyMembers", false);
Share:
13,559
Shasti
Author by

Shasti

Updated on June 04, 2022

Comments

  • Shasti
    Shasti almost 2 years

    I have a JSON with list of Objects and any of the item in the list can have null or the same object as a value for a key. I am looking for a faster way to parse the json to arrive at my final result. The data structure looks like -

    [
        {
            "firstName": "Bruce",
            "familyMembers": null
        },
        {
            "firstName": "Gates Family",
            "familyMembers": [
                {
                    "firstName": "Bill",
                    "familyMembers": null
                },
                {
                    "firstName": "Steve",
                    "familyMembers": null
                }
            ]
        },
        {
            "firstName": "Lee",
            "familyMembers": null
        },
        {
            "firstName": "Chan",
            "familyMembers": null
        }
    ]
    

    The output should be a set = ("Bruce", "Bill", "Steve", "Lee", "Chan").

    I am looking for a best possible way to do this in Java, such that i dont increase my response time by getting caught in this parsing hell. Appreciate your time on this.

    • Samuel Toh
      Samuel Toh almost 8 years
    • jtahlborn
      jtahlborn almost 8 years
      use jackson's stream parser.
    • Samuel Toh
      Samuel Toh almost 8 years
      On a side note. The file that you have up there does not comply to the JSON standard because the content should begin with the { } clause. E.g. { member: [ {1:2}, {3:4} ] }
    • Samuel Toh
      Samuel Toh almost 8 years
      Also, should the string "GatesFamily" be included into the set? Because that is reflected as the "firstName" attribute.