Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Turning nested JSON into an HTML nested list with Javascript

I'm fairly new to using JSON (as opposed to XML) and am currently working purely with Javascript to digest, parse and display my returned JSON data.

I'm using the JSON2.js library and am getting back some valid JSON data representing a fairly simple nested list:

{
  "node":{
    "class":"folder",
    "title":"Test Framework",
    "node":{
      "class":"folder",
      "title":"Item 1",
      "node":{
        "class":"folder",
        "title":"Item 1.1",
        "node":{
          "class":"file",
          "title":"Item 1.1.a"
        }
      },
      "node":{
        "class":"folder",
        "title":"Item 1.2",
        "node":{
          "class":"file",
          "title":"Item 1.2.a"
        },
        "node":{
          "class":"file",
          "title":"Item 1.2.b"
        },
        "node":{
          "class":"file",
          "title":"Item 1.2.c"
        }
      },
      "node":{
        "class":"folder",
        "title":"Item 1.3",
        "node":{
          "class":"folder",
          "title":"Item 1.3.a",
          "node":{
            "class":"file",
            "title":"Item 1.3.a.i"
          },
          "node":{
            "class":"file",
            "title":"Item 1.3.a.ii"
          }
        }
      }
    },
    "node":{
      "class":"folder",
      "title":"Item 2",
      "node":{
        "class":"file",
        "title":"item 2.a"
      },
      "node":{
        "class":"file",
        "title":"Item 2.b"
      }
    }
  }
}

Does anyone have any pointers for a quick way to turn that lot into a UL with all of the nested elements? It would be cool as well if the "class" element that's in the JSON could be used as the class for each LI.

Any help is much appreciated.

Thanks,

Dave.

like image 422
Dave Waller Avatar asked Jul 31 '09 10:07

Dave Waller


People also ask

How do I display nested JSON data in HTML?

In each iteration, we want to create a new li . Then, we append the key (for arrays, it's the index; for objects, it's your specified key when instantiating the object). And finally, we check if the item we want to print is another object (the typeof an array evaluates to object , so it works for arrays, too).

How fetch data from JSON to HTML?

The jQuery code uses getJSON() method to fetch the data from the file's location using an AJAX HTTP GET request. It takes two arguments. One is the location of the JSON file and the other is the function containing the JSON data. The each() function is used to iterate through all the objects in the array.

Can JSON have nested list?

A resource representation, in the JSON format, is a complex JSON object. Lists of items and nested data structures are represented as JSON arrays and nested JSON objects.

How can we parse a nested JSON object in Java?

We can parse a nested JSON object using the getString(index) method of JSONArray. This is a convenience method for the getJSONString(index). getString() method and it returns a string value at the specified position.


2 Answers

Your json is unsuited to your task. Some objects have several properties with the same name ("node"), so they are overriding one another. You have to use arrays of nodes instead. Here is a working data structure and the functions that can turn it into a nested list:

<!DOCTYPE HTML PUBLIC"-//W3C//DTD HTML 4.01//EN"
    "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title></title>
<script type="text/javascript">
function parseNodes(nodes) { // takes a nodes array and turns it into a <ol>
    var ol = document.createElement("OL");
    for(var i=0; i<nodes.length; i++) {
        ol.appendChild(parseNode(nodes[i]));
    }
    return ol;
}

function parseNode(node) { // takes a node object and turns it into a <li>
    var li = document.createElement("LI");
    li.innerHTML = node.title;
    li.className = node.class;
    if(node.nodes) li.appendChild(parseNodes(node.nodes));
    return li;
}

window.jsonData = [{
    "class": "folder",
    "title": "Test Framework",
    "nodes": [{
        "class": "folder",
        "title": "Item 1",
        "nodes": [{
            "class": "folder",
            "title": "Item 1.1",
            "nodes": [{
                "class": "file",
                "title": "Item 1.1.a"
            }]
        },
        {
            "class": "folder",
            "title": "Item 1.2",
            "nodes": [{
                "class": "file",
                "title": "Item 1.2.a"
            },
            {
                "class": "file",
                "title": "Item 1.2.b"
            },
            {
                "class": "file",
                "title": "Item 1.2.c"
            }]
        },
        {
            "class": "folder",
            "title": "Item 1.3",
            "nodes": [{
                "class": "folder",
                "title": "Item 1.3.a",
                "nodes": [{
                    "class": "file",
                    "title": "Item 1.3.a.i"
                },
                {
                    "class": "file",
                    "title": "Item 1.3.a.ii"
                }]
            }]
        }]
    },
    {
        "class": "folder",
        "title": "Item 2",
        "nodes": [{
            "class": "file",
            "title": "item 2.a"
        },
        {
            "class": "file",
            "title": "Item 2.b"
        }]
    }]
}];

</script>
</head>
<body>
    <input type="button" 
    onclick="document.body.appendChild(parseNodes(jsonData))"
    value="go" />
</body>
</html>

And I can add this css to have the items numberings match the node titles :)

<style type="text/css">
ol { list-style-type: none }
ol ol { list-style-type: decimal }
ol ol ol { list-style-type: decimal }
ol ol ol ol { list-style-type: lower-alpha }
ol ol ol ol ol { list-style-type: lower-roman }
</style>

See it in action.

like image 134
Alsciende Avatar answered Oct 18 '22 11:10

Alsciende


Here's some fairly simple code that creates a <ul> element for each object, and a <li> element for each portion. You can add a quick check to test the child variable against things like "class" if you want to add a special case.

function jsonToHtmlList(json) {
    return objToHtmlList(JSON.parse(json));
}

function objToHtmlList(obj) {
    if (obj instanceof Array) {
        var ol = document.createElement('ol');
        for (var child in obj) {
            var li = document.createElement('li');
            li.appendChild(objToHtmlList(obj[child]));
            ol.appendChild(li);
        }
        return ol;
    }
    else if (obj instanceof Object && !(obj instanceof String)) {
        var ul = document.createElement('ul');
        for (var child in obj) {
            var li = document.createElement('li');
            li.appendChild(document.createTextNode(child + ": "));
            li.appendChild(objToHtmlList(obj[child]));
            ul.appendChild(li);
        }
        return ul;
    }
    else {
        return document.createTextNode(obj);
    }
}

This won't do exactly what you want, because your JSON doesn't make sense. Objects in JavaScript, and therefore JSON, are maps, and so you can't have more than one child with the same name. You'll need to turn your multiple "node"s into an array, as Cédric points out.

like image 25
Samir Talwar Avatar answered Oct 18 '22 10:10

Samir Talwar