Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Display JSON data on a page as a expandable/collapsible list

I need help with with displaying JSON data on a page like expandable/collapsible list.

Here is a valid JSON I`ve made converting from XML with Python:

JSON Data

And to display it I`m usig this:

<!DOCTYPE HTML>
<head>
    <title>JSON Tree View</title>
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" type="text/javascript"></script>
    <script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.2/jquery-ui.min.js" type="text/javascript"></script>

</head>
<script>
function json_tree(object){
        var json="<ul>";
        for(prop in object){
            var value = object[prop];
            switch (typeof(value)){
                case "object":
                    var token = Math.random().toString(36).substr(2,16);
                    json += "<li><a class='label' href='#"+token+"' data-toggle='collapse'>"+prop+"="+value+"</a><div id='"+token+"' class='collapse'>"+json_tree(value)+"</div></li>";
                break;
                default:
                json += "<li>"+prop+"="+value+"</li>";
            }
        }
        return json+"</ul>";
}
</script>
<body style="margin: 40px;">
<h3>Paste JSON Into The Textarea Below and Click 'Build Tree'</h3>

<textarea id="json" style="width: 100%;min-height:300px;">

</textarea>
<button onclick="$('#output').html(json_tree(JSON.parse($('#json').val())));">Build Tree</button>
<div id="output">

</div>
</body>
</html>

This is what I get:

Image

I need help "filltering" (or merge with upper node) those "0" and "1", and also - how to show only values of the attributes without the names (or if do you have some better idea how can I dusplay this list)?

like image 571
MilicaJ Avatar asked Sep 13 '15 12:09

MilicaJ


People also ask

What is the best way to view a JSON file?

Cross-platform to open JSON files: Generally, users can open the JSON file in any text editor as it is a plain text-based file. The Google Chrome and Mozilla Firefox web browsers are cross-platform to open JSON files that are compatible with every operating system (OS).


2 Answers

Some of the links to the questions are no longer accessible. I assume you are looking for how to make a collapsible JSON view.


TL;DR

you can jump to Full code.

the code is very short (200 lines↓, including JSDoc, comment, test code.)

Inspire you on how to solve the problem.

This question in some skill is very much like how to make the table of contents. (TOC)

  1. First of all, JSON data is like an object. All we need to do is to add some more attributes (key, depth, children, ...) for each item.

  2. Once these actions are done, all left is to render, and here is the pseudo-code for rendering.

    render(node) {
      const divFlag = document.createRange().createContextualFragment(`<div style="margin-left:${node.depth * 18}px"></div>`)
      const divElem = divFlag.querySelector("div")
      const spanFlag = document.createRange().createContextualFragment(
        `<span class="ms-2">${node.key} : ${node.value}</span>`
      )
      node.children.forEach(subNode => {
        const subElem = render(subNode)
        spanFlag.append(subElem)
      })
      divElem.append(spanFlag)
      return divElem
    }
    

Full code

both CSS is not necessary.

  • bootstrap: ms-2 (margin start)
  • fontawesome: fa-caret-right, fa-caret-down if you don't want to use it, you can use β–Έ(25B8)β–Ύ(25BE) as the before contents

<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"
      integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossOrigin="anonymous">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css"
      integrity="sha512-1ycn6IcaQQ40/MKBW2W4Rhis/DbILU74C1vSrLJxCq57o941Ym01SwNsOMqvEBFlcgUa6xLiPY/NS5R+E6ztJQ=="
      crossOrigin="anonymous" referrerpolicy="no-referrer"/>

<script type="module">
  // πŸ‘‡ main script {Node, Tree, JsonView}
  class Node {
    /**
     * @description Add more attributes to the item.
     * @param {*} item
     * @param {*} key
     * @param {Node} parent
     * */
    constructor(item, key, parent) {
      this.key = key

      /** @param {string} */
      this.type = Array.isArray(item) ? "array" : typeof item

      /** @param {Number} */
      this.depth = parent ? parent.depth + 1 : 0
      this.value = item
      this.parent = parent

      /** @param {[Node]} */
      this.children = []
    }
  }

  class Tree {
    /**
     * @description Given the root node, it will complete the children of it.
     * @param {Node} rootNode
     */
    constructor(rootNode) {
      this.root = rootNode

      const obj = this.root.value
      if (!(obj instanceof Object)) { // Array is an Object too.
        return
      }
      Object.keys(obj).forEach(keyOrIdx => {
        const value = obj[keyOrIdx]
        const subNode = new Node(value, keyOrIdx, rootNode)
        const subTree = new Tree(subNode)
        rootNode.children.push(subTree.root)
      })
    }

    /**
     * @param {string | Object} jsonData
     * @return {Tree}
     */
    static CreateTree(jsonData) {
      jsonData = typeof jsonData === "string" ? JSON.parse(jsonData) : jsonData
      const rootNode = new Node(jsonData, "root", null)
      return new Tree(rootNode)
    }
  }

  class JsonView {
    static DefaultColorMap = {
      text: {
        string: "green",
        number: "#f9ae58",
        boolean: "#ca4ff8",
        array: "black",
        object: "black",
      },
      bg: {
        object: "none"
        // ... You can add more by yourself. They are like the text as above.
      }
    }

    static NewConfig() {
      return JSON.parse(JSON.stringify(JsonView.DefaultColorMap))
    }

    static SEPARATOR = " : "

    /** @type {Tree} */
    #tree

    /**
     * @param {Tree} tree
     * */
    constructor(tree) {
      this.#tree = tree
    }

    /**
     * @param {Node} node
     * @param {Object} colorMap
     */
    #render(node, colorMap = JsonView.DefaultColorMap) {
      /**
       * @param {Node} node
       * */
      const getValue = (node) => {
        const typeName = node.type
        switch (typeName) {
          case "object":
            return `object {${Object.keys(node.value).length}}`
          case "array":
            return `array [${Object.keys(node.value).length}]`
          default:
            return node.value
        }
      }

      const arrowIcon = ["object", "array"].includes(node.type) ? `<i class="fas fa-caret-down"></i>` : ""
      const divFlag = document.createRange().createContextualFragment(`<div style="margin-left:${node.depth * 18}px">${arrowIcon}</div>`)
      const divElem = divFlag.querySelector("div")

      const textColor = colorMap.text[node.type] !== undefined ? `color:${colorMap.text[node.type]}` : ""
      const bgColor = colorMap.bg[node.type] !== undefined ? `background-color:${colorMap.bg[node.type]}` : ""
      const valueStyle = (textColor + bgColor).length > 0 ? `style=${[textColor, bgColor].join(";")}` : ""

      const keyName = node.depth !== 0 ? node.key + JsonView.SEPARATOR : "" // depth = 0 its key is "root" which is created by the system, so ignore it.
      const spanFlag = document.createRange().createContextualFragment(
        `<span class="ms-2">${keyName}<span ${valueStyle}>${getValue(node)}</span></span>`
      )

      const isCollapsible = ["object", "array"].includes(node.type)

      node.children.forEach(subNode => {
        const subElem = this.#render(subNode, colorMap)

        if (isCollapsible) {
          divFlag.querySelector(`i`).addEventListener("click", (e) => {
            e.stopPropagation()
            subElem.dataset.toggle = subElem.dataset.toggle === undefined ? "none" :
              subElem.dataset.toggle === "none" ? "" : "none"

            e.target.className = subElem.dataset.toggle === "none" ? "fas fa-caret-right" : "fas fa-caret-down" // Change the icon to β†’ or ↓

            subElem.querySelectorAll(`*`).forEach(e => e.style.display = subElem.dataset.toggle)
          })
        }

        spanFlag.append(subElem)
      })
      divElem.append(spanFlag)
      return divElem
    }

    /**
     * @param {Element} targetElem
     * @param {?Object} colorMap
     */
    render(targetElem, colorMap = JsonView.DefaultColorMap) {
      targetElem.append(this.#render(this.#tree.root, colorMap))
    }
  }

  // πŸ‘‡ Below is Test
  function main(outputElem) {
    const testObj = {
      db: {
        port: 1234,
        name: "My db",
        tables: [
          {id: 1, name: "table 1"},
          {id: 2, name: "table 2"},
        ],
      },
      options: {
        debug: false,
        ui: true,
      },
      person: [
        "Foo", 
        "Bar"
      ]
    }
    const tree = Tree.CreateTree(testObj)
    const jsonView = new JsonView(tree)
    jsonView.render(outputElem)
    /* If you want to set the color by yourself, you can try as below
    const config = JsonView.NewConfig()
    config.bg.object = "red"
    jsonView.render(outputElem, config)
     */
  }

  (() => {
    window.onload = () => {
      main(document.body)
    }
  })()
</script>

vanilla JavaScript

like image 184
Carson Avatar answered Oct 16 '22 16:10

Carson


a good-looking, compact, collapsible tree view

pgrabovets' json-view is amazingly clean and well designed.

Check out the demo

like image 27
Inigo Avatar answered Oct 16 '22 15:10

Inigo