我需要在页面上显示 JSON 数据(如可展开/可折叠列表)方面的帮助。
这是我使用 Python 从 XML 转换而来的有效 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”,以及如何仅显示不带名称的属性值(或者您是否有更好的想法我该如何 重播此列表)?
如果您可以考虑使用 JS 库,请考虑使用 JSON Formatter 或 Render 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 视图。
您可以跳转到完整代码。
代码很短(200行↓,包括JSDoc、注释、测试代码。)
这个问题在某些技巧上很像如何制作目录。 (目录)
首先,JSON 数据就像一个对象。我们需要做的就是为每个项目添加更多属性(键、深度、子项……)。
完成这些操作后,剩下的就是渲染,这里是渲染的伪代码。
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
我使用了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'
}
}