0

Parent key set: -------[2002, 2003, 2000, 2001, 2004, 2005, 999]

I have a Map which has got many key/val pair as mentioned below. 2002 as key is having value as [201, 1004, 200]. Now, 201 in value is again being used as key in this map which has some value. So this structure goes on.

How can I build a JSON nested JSON structure using Java code?

Breaking condition: - when a value is not present as key. It is treated as leaf node without any child and we can close JSON strucure for that one.

{2002=[201, 1004, 200],
 2003=[1002, 311, 902, 312], 
 2001=[305, 304, 322, 900, 317, 301, 319, 302, 318, 303], 
 201=[203, 202], 
 203=[204], 
 2004=[310, 109], 
 202=[121], 
 2005=[1000, 1003, 116, 504, 115, 505, 114, 1010, 502, 108, 503, 107]}
7
  • raw data is string ? or in which format ? file etc. Commented Sep 9, 2014 at 3:43
  • So for 2002 you want something like {2002 = [ {201 = [ {203 = 204}, {202 = 121} ], 1004, 200] } ? Should 201, 203 and 202 still be top-level keys like 2002? Commented Sep 9, 2014 at 6:30
  • Yes you are right.201 and 203 and 202 also would be top level keys in similar fashion. Commented Sep 9, 2014 at 7:28
  • raw data u can treat as string. Commented Sep 9, 2014 at 7:29
  • Hi Soana Please ignore my previous comment. please consider only -[2002, 2003, 2000, 2001, 2004, 2005, 999] as the top element. Should 201, 203 and 202 still be top-level keys like 2002? – NO Commented Sep 9, 2014 at 7:37

2 Answers 2

1

Same preconditions as in my other answer apply (I created the same map)

To solve this non-recursively, I used a stack. For this I needed a class to hold all the information about a stack element:

public class StackElement{
    int value; //the value
    StackElement parent; //the object which corresponds to the key in the map
    boolean touched = false; //have my children been added to the stack?
    JSONArray jArr = new JSONArray(); //my children's JSON

    public StackElement(int value, StackElement parent){
        this.value = value;
        this.parent = parent;
    }

    public void addToParent(){
        if(jArr.length() == 0){
            //I have no children, so I only put my value
            parent.jArr.put(value);
        }
        else{
            parent.jArr.put(new JSONObject().put(Integer.toString(value), jArr));
        }
    }
}

For convenience I created another class for the top-level keys like 2002 and 999, so that I don't have to handle them differently

public class TopLevelStackElement extends StackElement{
    //corresponds to the top-level keys like 2002 and 999

    JSONObject jObj;
    public TopLevelStackElement(int value, JSONObject jObj) {
        super(value, null);
        this.jObj = jObj;
    }

    @Override
    public void addToParent(){
        //add the accumulated JSON of the children to the JSONObject 
        //I want to have as result
        jObj.put(Integer.toString(value), jArr);
    }

}

To create the JSONObject I used following algorithm:

JSONObject jObj = new JSONObject(); //the result
Stack<StackElement> stack = new Stack<>(); //stack to store the pending nodes
//add all the top-level keys
for(int i: new int[]{2002, 2003, 2000, 2001, 2004, 2005, 999}){
    stack.push(new TopLevelStackElement(i, jObj));
}

//process all the nodes
while(!stack.isEmpty()){
    StackElement item = stack.peek();

    if(item.touched || !map.containsKey(item.value)){
        //either item is a leaf, which means that the map does not contain it 
        //as a key
        //or its children had already been added to the stack and processed
        item.addToParent();
        stack.pop();
    }
    else{
        //the item is not a leaf, but has not been processed before, which means
        // that I have to put its children on the stack to be processed 
        //(see below for explanation)
        item.touched = true; //tells me that my children have been added
        Integer[] values = map.get(item.value);
        for(int i = 0; i < values.length; i++){
            //add all the children to the stack
            stack.push(new StackElement(values[i], item)); 
        }
    }
}

The idea works like this:

                                         121
         200                     202(f)  202(t)  202(t)
        1004    1004             203(f)  203(f)  203(f)  203(f)
         201(f)  201(f)  201(f)  201(t)  201(t)  201(t)  201(t)
2002(f) 2002(t) 2002(t) 2002(t) 2002(t) 2002(t) 2002(t) 2002(t)
   1.     2.      3.      4.      5.      6.      7.      8.

In step 5. the children of 202 are added to the stack (only 121), as seen in step 6. In step 7. all children of 202 have been processed, so I can remove 202 from the stack.

How do I know this, if I have only the 202 element and no other information?

Simple: I add a flag to the 202 element, which I set to true when I add its children. When I encouter an element for which there are values in the map, but which has this flag set to true, I know that the children have been processed and I can safely remove this item from the stack.

So the letter in brackets represents the current status of this flag.

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

Comments

0

Based on your question I have a map like this:

HashMap<Integer, Integer[]> map = new HashMap<>();
map.put(2002, new Integer[]{201, 1004, 200});
map.put(2003, new Integer[]{1002, 311, 902, 312});
map.put(2001, new Integer[]{305, 304, 322, 900, 317, 301, 319, 302, 318, 303}); 
map.put(201, new Integer[]{203, 202}); 
map.put(203, new Integer[]{204}); 
map.put(2004, new Integer[]{310, 109}); 
map.put(202, new Integer[]{121}); 
map.put(2005, new Integer[]{1000, 1003, 116, 504, 115, 505, 114, 1010, 502, 108, 503, 107});

To create the nested structure I will use a recursive method:

For the given keyValue I retrieve all values and look if they are a key in the map. If they are, I call the same method again with the value as keyValue, otherwise I will just return the value as it is a leaf.

private JSONArray createJSONArray(HashMap<Integer, Integer[]> map, int keyValue) {
    Integer[] values = map.get(keyValue);
    JSONArray arr = new JSONArray();
    if(values != null){
        for(int val: values){
            if(map.containsKey(val)){
                JSONObject jObj = new JSONObject();
                jObj.put(Integer.toString(val), createJSONArray(map, val));
                arr.put(jObj);
            }
            else{
                arr.put(val);
            }
        }
    }
    return arr;
}

To create a complete JSONObject with the top-level keys, I run the method above in a loop with the top-level values as keyValue:

JSONObject jObj = new JSONObject();
for(int key: new int[]{2002, 2003, 2000, 2001, 2004, 2005, 999}){
    jObj.put(Integer.toString(key), createJSONArray(map, key));
}

This will result in the following, which is hopefully the format you wanted:

{
    "2004": [ 310, 109 ],
    "2005": [ 1000, 1003, 116, 504, 115, 505, 114, 1010, 502, 108, 503, 107 ],
    "2002": [ {"201": [ {"203": [204] }, {"202": [121] } ] }, 1004, 200 ],
    "2003": [ 1002, 311, 902, 312 ],
    "2001": [ 305, 304, 322, 900, 317, 301, 319, 302, 318, 303 ],
    "999": [],
    "2000": []
}

5 Comments

jObj.put(Integer.toString(val), createJSONObject(map, val)); HI i think createJSONArray(Integer.toString(val), createJSONObject(map, val)) shold be the method name. or you have seperate createJSONObject(map, val) method defined?
@Reeteshsingh Sorry, I renamed the function at some point and forgot this occurance. I fixed it.
Hi standalone it is working. now when i put in real time code . i m getting stackoverflow error. can it be written without recursion?
@rks are you sure that your real data does not contain loops in the references? like 1 = [2, 3, 4], 2 = [1, 3]
@rks see my second answer

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.