纯JS实现任意深度JSON重构为带name和children的树形结构
JSON结构转树形格式实现方案
两种纯JavaScript实现方式均支持任意嵌套深度的JSON结构转换,输出完全符合要求的name+children结构。
递归实现
核心逻辑是按值的类型分发处理逻辑,遇到嵌套对象/数组时递归生成子节点,代码简洁易读,适配绝大多数常规场景:
function transformToTree(input, rootName = 'data') { // 生成基础类型叶子节点 const createLeafNode = (value) => ({ name: String(value) // 若业务要求所有节点必须带children属性,放开下一行注释即可 // , children: [] }) // 单节点递归处理逻辑 const processNode = (key, value) => { const currentNode = { name: key, children: [] } // 处理普通对象:遍历所有键值对递归生成子节点 if (value !== null && typeof value === 'object' && !Array.isArray(value)) { Object.entries(value).forEach(([childKey, childValue]) => { currentNode.children.push(processNode(childKey, childValue)) }) return currentNode } // 处理数组:遍历每一项,基础值生成叶子,嵌套结构继续递归 if (Array.isArray(value)) { value.forEach((item, index) => { if (item !== null && typeof item === 'object') { // 数组内无明确键名的对象/数组,默认用索引作为节点name,可按业务需求调整 currentNode.children.push(processNode(String(index), item)) } else { currentNode.children.push(createLeafNode(item)) } }) return currentNode } // 处理基础类型值:挂载到当前节点的子节点列表 currentNode.children.push(createLeafNode(value)) return currentNode } // 初始化根节点 const root = { name: rootName, children: [] } Object.entries(input).forEach(([key, value]) => { root.children.push(processNode(key, value)) }) return root } // 测试调用 const rawJson = { "varA":true, "varB":false, "varC": { "time":"15:00:00", "date":"Jul 10", "items":["apple", "banana"] } } console.log(transformToTree(rawJson)) // 输出与目标结构完全一致
迭代实现
递归依赖JS引擎的函数调用栈,处理超深嵌套(比如层级过万)时可能触发栈溢出,用手动维护栈的迭代方式可以规避这个问题,逻辑和递归完全等价:
function transformToTreeIterative(input, rootName = 'data') { const createLeaf = (val) => ({ name: String(val) // 若业务要求所有节点必须带children属性,放开下一行注释即可 // , children: [] }) // 初始化根节点 const root = { name: rootName, children: [] } // 手动维护处理栈,栈元素格式为[当前待挂载节点, 待处理的值] const stack = [] // 根节点下的一级属性先入栈 Object.entries(input).forEach(([key, value]) => { const node = { name: key, children: [] } root.children.push(node) stack.push([node, value]) }) while (stack.length) { const [currentNode, currentValue] = stack.pop() // 处理普通对象 if (currentValue !== null && typeof currentValue === 'object' && !Array.isArray(currentValue)) { Object.entries(currentValue).forEach(([childKey, childVal]) => { const childNode = { name: childKey, children: [] } currentNode.children.push(childNode) stack.push([childNode, childVal]) }) continue } // 处理数组 if (Array.isArray(currentValue)) { currentValue.forEach((item, idx) => { if (item !== null && typeof item === 'object') { const childNode = { name: String(idx), children: [] } currentNode.children.push(childNode) stack.push([childNode, item]) } else { currentNode.children.push(createLeaf(item)) } }) continue } // 处理基础类型 currentNode.children.push(createLeaf(currentValue)) } return root }
注意事项
- 两种实现默认将布尔、数字等基础类型值转为字符串作为叶子节点的name,和示例输出规则一致
- 数组内的嵌套对象默认用索引作为name标识,可根据业务需求调整为自定义标识规则
- 如果要求所有节点(包括叶子节点)必须携带空
children数组,放开代码中对应位置的注释即可
内容的提问来源于stack exchange,提问作者gubith
相关产品推荐
相关产品推荐

