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

如何将嵌套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(/&lt;/g, '<')
    .replace(/&gt;/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 = `&lt;ul&gt;&lt;li&gt;&lt;p&gt;one&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;two&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;a&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;b&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;c&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;`;
console.log(html_to_data(html_string));

运行上述代码会输出和ideal_data_output完全一致的结构,支持任意深度的ul/li嵌套。

关键逻辑调整点

  • 移除了原逻辑中进入ul就无返回递归的写法,改用栈维护层级,遇到闭合标签自动回退,不会丢失上层数据
  • 修正了ul的归属逻辑:ul永远属于它外层的li条目,不会生成独立的无效对象
  • 补全了li标签的解析逻辑,每个li对应一个结构化条目,p标签提取的name值会正确绑定到对应条目

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 02:45:30