如何从无父子ID的扁平JSON在网页生成动态树形结构?
动态构建树形结构:处理无父子ID的JSON数据并忽略Null值
你现在遇到的问题是要把扁平化的JSON数据转换成嵌套树形结构,还要忽略null值,而且数据里没有父子ID字段对吧?先来看你提供的原始数据:
var projectViewData = [ { "projectTYpe": "Report", "Doctor": "Abc", "Patient": null, "type": "xyz", "document":"a.xls" }, { "projectTYpe": "Report", "Doctor": "Abc", "Patient": "Smith", "type": "xyz", "document":"a.xls" }, { "projectTYpe": "Analysis", "Doctor": "Abc", "Patient": null, "type": "xyz", "document":"a.xls" }, { "projectTYpe": "Report", "Doctor": "Abc", "Patient": "Smith", "type": "xyz", "document":"a.xls" } ];
你的思路是遍历每个对象拼接HTML,但现有代码的问题在于没有先构建结构化的树形数据,直接拼接字符串导致层级混乱,而且无法正确处理重复的节点。我给你整理一套清晰的解决方案:
第一步:先构建树形数据结构
首先要把扁平化的数据转换成嵌套的树形结构,这样后续生成HTML就会非常简单。我们可以定义好层级顺序(比如projectTYpe → Doctor → Patient → type → document),然后遍历每个数据项,逐步创建节点:
function buildTree(data) { const root = {}; // 定义树形的层级顺序,可根据需求调整 const hierarchy = ['projectTYpe', 'Doctor', 'Patient', 'type']; const leafKey = 'document'; data.forEach(item => { let currentNode = root; // 遍历层级,创建父节点 hierarchy.forEach(key => { const value = item[key]; if (value === null) return; // 跳过null值的层级 if (!currentNode[value]) { currentNode[value] = { children: {}, docs: [] }; } currentNode = currentNode[value].children; }); // 添加叶子节点(文档),并去重 const parentNode = getParentNode(root, item, hierarchy); if (!parentNode.docs.includes(item[leafKey])) { parentNode.docs.push(item[leafKey]); } }); // 辅助函数:找到当前项对应的父节点 function getParentNode(root, item, hierarchy) { let node = root; for (let i = 0; i < hierarchy.length; i++) { const key = hierarchy[i]; const value = item[key]; if (value === null) break; node = node[value]; } return node; } return root; } // 生成树形数据 const treeStructure = buildTree(projectViewData);
第二步:递归生成HTML结构
有了结构化的树形数据后,用递归的方式生成HTML,这样能完美处理嵌套层级,避免标签不闭合的问题:
function renderTree(node) { let html = '<ul>'; Object.keys(node).forEach(key => { const current = node[key]; html += `<li>${key}`; // 如果有子节点,递归渲染 if (Object.keys(current.children).length > 0) { html += renderTree(current.children); } // 如果有文档,渲染文档列表 if (current.docs.length > 0) { html += '<ul>'; current.docs.forEach(doc => { html += `<li>${doc}</li>`; }); html += '</ul>'; } html += '</li>'; }); html += '</ul>'; return html; } // 渲染树形结构到页面 const treeHTML = '<ul class="tree">' + renderTree(treeStructure).replace('<ul>', '').replace('</ul>', '') + '</ul>'; $("#list1").html(treeHTML);
为什么你的原有代码会出问题?
- 直接拼接HTML逻辑混乱:你试图用数组拼接HTML,但没有处理重复的父节点(比如多个数据里的
Report、Abc),导致会重复创建相同节点。 - 层级闭合错误:用
array.indexOf判断节点是否存在是无效的,因为数组里存的是HTML字符串,不是纯值,无法正确识别已存在的层级。 - 没有结构化数据:先构建树形数据再渲染,是处理这类问题的标准思路,能让逻辑更清晰,也方便后续维护和扩展。
额外优化建议
- 可以通过修改
hierarchy数组灵活调整树形的层级顺序,不需要改动核心逻辑。 - 如果不需要文档去重,可以去掉
!parentNode.docs.includes(item[leafKey])的判断。 - 可以给叶子节点(文档)添加特殊的CSS类,方便样式定制。
内容的提问来源于stack exchange,提问作者Vikas
相关产品推荐
相关产品推荐

