【问题标题】:Display JSON data on a page as a expandable/collapsible list在页面上将 JSON 数据显示为可展开/可折叠的列表
【发布时间】:2015-12-09 13:15:50
【问题描述】:

我需要有关在可展开/可折叠列表等页面上显示 JSON 数据的帮助。

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

JSON Data

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

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

这是我得到的:

Image

我需要帮助“填充”(或与上节点合并)那些“0”和“1”,以及 - 如何只显示没有名称的属性值(或者如果你有更好的想法怎么能一世 显示此列表)?

【问题讨论】:

  • 请不要使用图片,而是在问题中写示例代码(一段)。
  • 我不知道它是否有帮助,但我会使用您的浏览器的调试器和检查器来查看这些对象字符串表示,并查看哪些方法/属性可用于更漂亮地显示它们。跨度>
  • 要添加到现有答案,如果 jQuery 是可以接受的,我发现以下具有相当好的设计和功能:github.com/abodelot/jquery.json-viewer

标签: javascript python json list tree


【解决方案1】:

美观、紧凑、可折叠的树状视图

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

查看the demo

【讨论】:

    【解决方案2】:

    如果您可以考虑使用 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>
    

    【讨论】:

    • 渲染 json 效果很好!感谢您的回答!对于任何寻找脚本标签以直接从 github 添加脚本的人:
    【解决方案3】:

    部分问题的链接不再可用。我假设您正在寻找如何制作可折叠的 JSON 视图。


    TL;DR

    您可以跳转到完整代码

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

    启发你如何解决问题。

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

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

    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/bootstrap@5.0.2/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>

    原生 JavaScript

    【讨论】:

    • 酷,感谢分享东西。一点补充。当一个值为 null 时,它会抛出异常。所以可以在switch (typeName)之后加上-if(node.value) ...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-15
    • 1970-01-01
    • 2018-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多