在页面上将 JSON 数据显示为可展开/可折叠列表

问题描述 投票:0回答:4

我需要在页面上显示 JSON 数据(如可展开/可折叠列表)方面的帮助。

这是我使用 Python 从 XML 转换而来的有效 JSON:

JSON 数据

为了显示它,我使用这个:

<!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>

这就是我得到的:

图片

我需要帮助“填充”(或与上节点合并)那些“0”和“1”,以及如何仅显示不带名称的属性值(或者您是否有更好的想法我该如何 重播此列表)?

javascript python json list tree
4个回答
19
投票

a 好看、紧凑、可折叠的树视图

pgrabovets'

json-view
非常干净且设计精良。

查看演示


10
投票

如果您可以考虑使用 JS 库,请考虑使用 JSON FormatterRender JSON。 这两个库都提供主题、最大深度和排序等配置选项。 要使用 Render JSON 以可折叠形式显示简单的 JSON 字符串,您可以使用

<script>
    document.getElementById("test").appendChild(
        renderjson({ hello: [1,2,3,4], there: { a:1, b:2, c:["hello", null] } })
    );
</script>

8
投票

一些问题的链接无法再访问。我假设您正在寻找如何制作可折叠的 JSON 视图。


TL;博士

您可以跳转到完整代码

代码很短(200行↓,包括JSDoc、注释、测试代码。)

启发您如何解决问题。

这个问题在某些技巧上很像如何制作目录。 (目录)

  1. 首先,JSON 数据就像一个对象。我们需要做的就是为每个项目添加更多属性(键、深度、子项……)。

  2. 完成这些操作后,剩下的就是渲染,这里是渲染的伪代码。

    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
    }
    

完整代码

这两个 CSS 都不是必需的。

<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} */
      if (item !== null) {
        this.type = Array.isArray(item) ? "array" : typeof item
      } else {
        // typeof null => object, so we need to change it to "null"
        this.type = "null"
      }

      /** @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: "#56a3c9",
        null: "#f9ae58",
        boolean: "#ca4ff8",
        array: "black",
        object: "black",
      },
      bg: {
        object: undefined,
        // ... 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}]`
          case "null":
            return null
          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,
        test_null: null
      },
      person: [
        "Foo",
        "Bar",
        null
      ]
    }
    const tree = Tree.CreateTree(testObj)
    const jsonView = new JsonView(tree)
    jsonView.render(outputElem)
    /* If you want to set the color by yourself, you can replace `jsonView.render(outputElem)` as below
    const config = JsonView.NewConfig()
    config.bg.object = "red"
    jsonView.render(outputElem, config)
     */
  }

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

普通 JavaScript


1
投票

我使用了Carson Reply,它的工作原理就像魅力,但正如woto所说,存在空对象问题。 脚本发生变化。

const getValue = (node) => {
        const typeName = node.type
        switch (typeName) {
          case "object":
            if (node.value !== null)
                return `object {${Object.keys(node.value).length}}`
            else
                return 'null'
          case "array":
            if (node.value !== null)
                return `array {${Object.keys(node.value).length}}`
            else
                return 'null'
          default:
            if (node.value !== null)
                return node.value
            else
                return 'null'
        }
      }
© www.soinside.com 2019 - 2024. All rights reserved.