Creating a tree using json from a list/table

13,341

You should create classes to model your data, in the structure you require. You are basically wanting to build a hierarchical structure from some row based data, this is quite like an XML document, which might be an appropriate solution. But you got me hooked so I played about with what I had before and came up with this:

public class Test { 

    public static void main(String[] args) 
    {
        // hierarchical data in a flattened list
        String[][] data = {
                {"Toyota", "Gas", "Compact", "Corolla"},
                {"Toyota", "Gas", "Compact", "Camry"},
                {"Toyota", "Hybrid", "Compact", "Prius"},
                {"Honda", "Gas", "Compact", "Civic"}
        };

        TreeManager treeManager = new TreeManager();

        for(String[] row : data)
        {
            // build the path to our items in the tree
            List<String> path = new ArrayList<String>();
            for(String item : row)
            {
                // add this item to our path
                path.add(item);
                // will add it unless an Item with this name already exists at this path
                treeManager.addData(treeManager, path);
            }
        }

        treeManager.getData(data[0]).putValue("MPG", 38);
        treeManager.getData(data[1]).putValue("MPG", 28);

        Gson gson = new Gson();

        System.out.println(gson.toJson(treeManager));
    }

    /**
     * This base class provides the hierarchical property of
     * an object that contains a Map of child objects of the same type.
     * It also has a field - Name
     *
     */
    public static abstract class TreeItem implements Iterable<TreeItem>{

        private Map<String, TreeItem> children;     
        private String name;

        public TreeItem() {
            children = new HashMap<String, TreeItem>();
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public void addChild(String key, TreeItem data) 
        {           
            children.put(key, data);
        }

        public TreeItem getChild(String key) 
        {           
            return children.get(key);
        }

        public boolean hasChild(String key) 
        {           
            return children.containsKey(key);
        }

        @Override
        public Iterator<TreeItem> iterator() {          
            return children.values().iterator();
        }           
    }

    /**
     * This is our special case, root node. It is a TreeItem in itself
     * but contains methods for building and retrieving items from our tree
     *
     */
    public static class TreeManager extends TreeItem
    {       
        /**
         * Will add an Item to the tree at the specified path with the value
         * equal to the last item in the path, unless that Item already exists 
         */
        public void addData(List<String> path)
        {
            addData(this, path);
        }

        private void addData(TreeItem parent, List<String> path)
        {
            // if we're at the end of the path - create a node
            String data = path.get(0);
            if(path.size() == 1)
            {
                // unless there is already a node with this name
                if(!parent.hasChild(data))
                {
                    Group group = new Group();
                    group.setName(data);
                    parent.addChild(data, group);
                }
            }
            else
            {
                // pass the tail of this path down to the next level in the hierarchy
                addData(parent.getChild(data), path.subList(1, path.size()));
            }
        }

        public Group getData(String[] path)
        {
            return (Group) getData(this, Arrays.asList(path));
        }

        public Group getData(List<String> path)
        {
            return (Group) getData(this, path);
        }

        private TreeItem getData(TreeItem parent, List<String> path)
        {
            if(parent == null || path.size() == 0)
            {
                throw new IllegalArgumentException("Invalid path specified in getData, remainder: " 
                        + Arrays.toString(path.toArray()));
            }
            String data = path.get(0);
            if(path.size() == 1)
            {
                return parent.getChild(data);
            }
            else
            {
                // pass the tail of this path down to the next level in the hierarchy
                return getData(parent.getChild(data), path.subList(1, path.size()));
            }
        }
    }

    public static class Group extends TreeItem {

        private Map<String, Object> properties;

        public Object getValue(Object key) {
            return properties.get(key);
        }

        public Object putValue(String key, Object value) {
            return properties.put(key, value);
        }

        public Group () {
            super();
            properties = new HashMap<String, Object>();
        }       
    }
}

I think this meets most of the requirements you've mentioned so far, although I left out the averaging of the MPG values as an exercise for the reader (I've only got so much time...). This solution is very generic - you may want more concrete sub-classes that better describe your data model (like Manufacturer, Type, Model), as you will then be able to hang more useful methods off them (like calculating averages of fields in child objects) , and you wouldn't have to deal with the properties as a collection of Objects, but you then get more complicated code initialising your data structure from the list. Note - this is not production ready code, I've just provided it as an example of how you might go about modelling your data in Java.

If you are new to not only Java but Object Orientated Programming then you should read up on the subject. The code I have written here is not perfect, I can already see ways it could be improved. Learning to write good quality object orientated code takes time and practice. Read up on Design Patterns and Code Smells .

Share:
13,341
Admin
Author by

Admin

Updated on June 05, 2022

Comments

  • Admin
    Admin almost 2 years

    Let’s say I have table/list like this n=3 in this case, but n can be as unlimited.

    groupid       answerid1     answerid2     answerid(n)
    1              3            6             8 
    1              3            6             9 
    1              4            7               
    2              5                            
    

    and i want to create a parent/child tree json output like this using java.(I have been using GSON)

        {
            data: [
                {
                    groupid: 1,
                    children: [
                        {
                            answerid1: 1,
                            children: [
                                {
                                    answerid2:3,
                                    children: [
                                               {
                                        answerid3:6,
                                                      children: [
                                                                  {answerid4: 8},
                                      {answerid4: 9} 
                                                                 ]
                                                  } 
    
    
                                }, {
                                     answerid2: 4,
                                     children: [
                                              {answerid3:7} 
                                       ]
                                     }
                                    ]         
                        }, 
    
                   {
                     groupid1: 2,
                     children: [
                           { answerid2: 5}
                            ]
                    }
    
                   ]      
            }
    

    what would be the code/steps to do so. i have looked through lots of tags but mostly people are printing the output and not recursively build a hashmap/ArrayList for GSON to parse adn write to API. one other point each id has other data associated with it that will have to be included in the json output. for instance instead of {groupid:1} would need to this {groupid:1, text=toyota}.

    any help is greatly appreciated as i am fairly new to java as i come from SAS background.

    I get data like this (just a matrix of list) Toyota, Gas, Compact, Corolla

  • Toyota, Gas, Compact, Camry
  • Toyota, Hybrid, Compact, Prius
  • Honda, Gas, Compact, Civic
  • If needed I can REFORMAT THE DATA into two tables

    parentId parText answerId

  • 1 Toyota 1
  • 1 Toyota 2
  • 1 Toyota 3
  • 2 Honda 4
  • answerId level answerTextid answerText

  • 1 1 1 Gas
  • 1 2 2 Compact
  • 1 3 3 Corolla
  • 2 1 1 Gas
  • 2 2 2 Compact
  • 2 3 4 Camry
  • Then I need to make it a tree(nested output like the JSON shows with parent/children - just like if you were creatign a file system directory)

    one other thign i would like to do is for each car have mileage as a varialbe ({answerid3:4, text=Corolla, mileage=38}. but also if i traverse up the tree give an average mile for the branch. Like say at branch Toyota, Gas, Compact the mileage would be avg(Camry, Corolla)

    the output is a little off, i am looking for something like this. if no children then no children arraylist, and attrbutes are part of one object (hashmap)

    {"data":[{"id":1,"children":
        [{"id": 2,"children":
            [{"id": 3 ,"children":
                [{"id": 4,"name":"Prius"}],"name":"Compact"}],"name":"Hybrid"},
        {"id":5,"children":
            [{"id":3,"children":
                [{"id":7,"MPG":38, "name":"Corolla"},
                 {"id":8,"MPG":28,"name":"Camry"}],"name":"Compact"}],"name":"Gas"}],"name":"Toyota"},
    {"id":9, "children":
        [{"id":10,"children":
            [{"id":3 ,"children":
                [{"id":11 ,"name":"Civic"}],"name":"Compact"}],"name":"Gas"}],"name":"Honda"}]}
    
  • Admin
    Admin almost 12 years
    i appreciate the help. but what do you mean by creating an abstract super class that provides the (object that has a list of answers) property, and create your Group and Answer objects by extending it. to me i think this what i have been trying to do but ot know avail. i have been trying to use hashmaps and arraylist to make one "big " object and have GSON parse it.
  • Admin
    Admin almost 12 years
    on your code above you add to each answer array list but how to you loop through the initial "list" to get the desired output? this is how i been doing it where levels is the "levels" of each row HashMap map = new HashMap (); for (int i = 0; i < list.size(); i++){ HashMap item = (HashMap) list.get(i); int levId = (Integer)item.get("levels"); for (int lev = 0; lev < levId ; lev++) {
  • Malcolm Smith
    Malcolm Smith almost 12 years
    I've updated the example to show you what I mean by an abstract super class. I think what I'm missing here is an understanding of how your data comes in and how it is structured. You input seems to be a list of groups that contain a list of answers (single level - Just a List of Lists, but in your output JSON you have a Group with nested levels of answers within answers, and I can't quite see how you are wanting to get form one to the other. If you have more detail please update your original question.
  • Admin
    Admin almost 12 years
    thanks a bunch for the help for the object oriented link. my expericne is mostly VB (VBA), SAS, SQL programming. if you ever need any stat/math, SAS, SQkl help hit me up i will be glad to return the favor.
  • Malcolm Smith
    Malcolm Smith almost 12 years
    No worries, you can simply show your gratitude by upvoting and accepting my answer :)
  • Admin
    Admin almost 12 years
    i will as soon as i get enough points (only at 11 - likei said new)
  • Malcolm Smith
    Malcolm Smith almost 12 years
    I'm pretty sure you can accept answers from the word go.... you won't be able to up vote though
  • Admin
    Admin almost 12 years
    still looking at a few thigns on the answer. the recusion works 100% but the way it comes out is off some. each time a new children is present i need a new arraylist so it has to be [ instead of {.