Shasti
Shasti

Reputation: 91

Parse JSON object recursively - Java

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.

Upvotes: 2

Views: 16793

Answers (3)

Samuel Toh
Samuel Toh

Reputation: 19268

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

Upvotes: 1

Jeffrey Chen
Jeffrey Chen

Reputation: 1967

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

Upvotes: 1

abdulrafique
abdulrafique

Reputation: 304

You can use ObjectMapper defined in jackson-databind-2.6.5.jar

Define a java class with fields similar to json pattern and then just bind them like:

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;   

 ObjectMapper objectMapper = new ObjectMapper();
 objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
 objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
 Family family=objectMapper.readValue(jsonString, Family.class);

Now you will have a java object similar to your json string pattern and you can print it the way you like.

Upvotes: 0

Related Questions