You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从无父子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);

为什么你的原有代码会出问题?

  1. 直接拼接HTML逻辑混乱:你试图用数组拼接HTML,但没有处理重复的父节点(比如多个数据里的Report、Abc),导致会重复创建相同节点。
  2. 层级闭合错误:用array.indexOf判断节点是否存在是无效的,因为数组里存的是HTML字符串,不是纯值,无法正确识别已存在的层级。
  3. 没有结构化数据:先构建树形数据再渲染,是处理这类问题的标准思路,能让逻辑更清晰,也方便后续维护和扩展。

额外优化建议

  • 可以通过修改hierarchy数组灵活调整树形的层级顺序,不需要改动核心逻辑。
  • 如果不需要文档去重,可以去掉!parentNode.docs.includes(item[leafKey])的判断。
  • 可以给叶子节点(文档)添加特殊的CSS类,方便样式定制。

内容的提问来源于stack exchange,提问作者Vikas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 09:25:51