JSON转树形结构的核心是依据id与parentId等父子字段递归或迭代构建嵌套对象,关键在于层级关系组织而非JSON解析本身;推荐用Map建立节点索引以提升性能,支持多种父子标识方式,并需处理空值、类型匹配及循环引用等边界情况。

JSON 数据转树形结构,核心是根据父子关系字段(比如 id 和 parentId)递归或迭代构建嵌套对象。关键不在于 JSON 解析本身(JSON.parse() 只负责转成普通对象数组),而在于如何按层级关系组织数据。
明确父子关联字段
树形结构依赖清晰的层级标识。常见字段组合有:
- id + parentId(最常用,parentId 为 null/0 表示根节点)
- id + parent(parent 是父节点的 id 字符串或对象)
-
path 字段(如
"1/3/8",通过分割路径推导层级) - 无显式 parentId,但靠 children 数组反向推断(需先扁平化再重组)
若原始 JSON 没有这些字段,需先预处理补充(例如后端返回平铺列表但约定规则)。
用 Map 一次性建立节点索引(推荐)
避免重复遍历,提升性能。适合大多数 id/parentId 场景:
立即学习“Java免费学习笔记(深入)”;
function listToTree(list, idKey = 'id', parentKey = 'parentId', childrenKey = 'children') {
const nodeMap = new Map();
const roots = [];
<p>// 第一遍:全部节点存入 Map
list.forEach(item => {
nodeMap.set(item[idKey], { ...item, [childrenKey]: [] });
});</p><p>// 第二遍:挂载子节点
list.forEach(item => {
const parentId = item[parentKey];
const node = nodeMap.get(item[idKey]);</p><pre class="brush:php;toolbar:false;">if (parentId == null || parentId === '' || parentId === 0) {
roots.push(node);
} else {
const parent = nodeMap.get(parentId);
if (parent) parent[childrenKey].push(node);
}});
return roots; }
调用示例:const tree = listToTree(jsonArray);
输入是普通数组(已 JSON.parse()),输出即树形结构数组(根节点集合)。
递归方式(适合小数据或路径法)
如果数据量小,或用 path 字段,递归更直观:
function buildTreeByPath(list, pathKey = 'path', separator = '/') {
const sorted = [...list].sort((a, b) => a[pathKey].length - b[pathKey].length);
const nodeMap = new Map();
<p>sorted.forEach(item => {
const path = item[pathKey];
const parts = path.split(separator).filter(p => p);
const lastId = parts[parts.length - 1];</p><pre class="brush:php;toolbar:false;">const node = { ...item, children: [] };
nodeMap.set(lastId, node);
if (parts.length > 1) {
const parentId = parts[parts.length - 2];
const parent = nodeMap.get(parentId);
if (parent) parent.children.push(node);
}});
return Array.from(nodeMap.values()).filter(n => !n[pathKey].includes(separator)); }
注意:此方式依赖 path 的层级顺序,需确保父路径一定比子路径先出现或排序处理。
注意事项和边界情况
实际使用中容易忽略这些点:
- 检查 parentId 值类型是否匹配(字符串 "1" 和数字 1 不等)
- 根节点判定要兼容
null、undefined、0、""等多种空值写法 - 循环引用检测(如 A 的 parentId 是 B,B 的 parentId 是 A)——可加 visited Set 防止死循环
- 多个根节点是正常情况,返回的是数组;如需单根,可包装一层
{ id: 'root', children: result }
不复杂但容易忽略细节,理清数据关系再选方法,基本就能稳定转换。


















