如何将嵌套ul/li结构的HTML字符串转换为指定格式JS对象?
嵌套HTML列表转结构化数据实现方案
原有代码核心问题
- 缺失
li标签、所有闭合标签(/ul//li//p)的解析逻辑,无法识别条目边界和层级结束位置 - 对
ul标签的处理逻辑错误:遇到ul就直接创建带list属性的独立对象插入当前数组,实际上嵌套ul从属于它所在的li条目,list属性应该绑定到对应li的对象上 - 递归逻辑没有维护层级上下文,进入子列表后没有回退到父级的机制,最终只会返回最深层的列表内容,也就是你得到的
a/b/c三个条目
嵌套结构解析通用思路
这类多层级嵌套结构解析,最稳定的方案是上下文栈机制,不要用无状态的盲目递归:
- 遍历标签过程中,用栈存储每一层的挂载目标(当前要插入内容的数组、当前正在构建的条目对象)
- 遇到开标签时,根据标签类型创建对应结构,把旧的上下文压入栈,切换到新的上下文
- 遇到闭合标签时,从栈顶弹出旧的上下文,切回上层继续解析,自动完成层级回退
- 不要跳过任何标签类型的判断,明确每个标签对应的结构含义:
li对应单条数据对象,p内是name值,ul代表当前条目存在子列表,闭合标签代表当前层级解析结束
修正后的可运行代码
const html_to_data = (html_string) => { // 先转义HTML实体,拆分标签并过滤空片段 const tagList = html_string .replace(/</g, '<') .replace(/>/g, '>') .split('<') .filter(Boolean); const result = []; // 栈存储上层上下文,每个元素保存上层的挂载数组、上层正在构建的条目 const contextStack = []; // 当前挂载的数组、当前正在构建的li条目对象 let currentList = result; let currentItem = null; for (const tagFragment of tagList) { const [tagName, ...contentParts] = tagFragment.split('>'); const content = contentParts.join('>').trim(); switch(tagName) { case 'ul': // 遇到ul开标签:给当前条目绑定list属性,切换上下文到子列表 if (currentItem) { currentItem.list = []; contextStack.push({ list: currentList, item: currentItem }); currentList = currentItem.list; currentItem = null; } break; case '/ul': // 遇到ul闭合标签:弹出上层上下文,回到父级 const parentCtx = contextStack.pop(); if (parentCtx) { currentList = parentCtx.list; currentItem = parentCtx.item; } break; case 'li': // 遇到li开标签:新建条目对象,挂载到当前列表 currentItem = { name: null }; currentList.push(currentItem); break; case '/li': // 遇到li闭合标签:清空当前构建的条目 currentItem = null; break; case 'p': // 遇到p开标签:提取文本作为当前条目的name if (currentItem) currentItem.name = content; break; } } return result; }; // 测试输入 let html_string = `<ul><li><p>one</p></li><li><p>two</p><ul><li><p>a</p></li><li><p>b</p></li><li><p>c</p></li></ul></li></ul>`; console.log(html_to_data(html_string));
运行上述代码会输出和ideal_data_output完全一致的结构,支持任意深度的ul/li嵌套。
关键逻辑调整点
- 移除了原逻辑中进入ul就无返回递归的写法,改用栈维护层级,遇到闭合标签自动回退,不会丢失上层数据
- 修正了ul的归属逻辑:ul永远属于它外层的li条目,不会生成独立的无效对象
- 补全了li标签的解析逻辑,每个li对应一个结构化条目,p标签提取的name值会正确绑定到对应条目
内容的提问来源于stack exchange,提问作者user_1123
相关产品推荐
相关产品推荐

