张腾岳 2025-12-04 02:00 采纳率: 98.8%
浏览 0
已采纳

富文本转JSON时标签嵌套如何处理?

在将富文本转换为JSON结构时,常需处理HTML标签的嵌套关系。典型问题是:当遇到重叠或非闭合的标签(如 `Hello`)时,如何正确还原层级结构并保证JSON的嵌套逻辑不混乱?若直接按开闭标签匹配,易导致父子节点错位,影响后续渲染。因此,需设计栈结构维护标签层级,并结合容错机制智能闭合或调整嵌套顺序,确保输出JSON语义准确、结构合规。
  • 写回答

1条回答 默认 最新

  • 小丸子书单 2025-12-04 08:53
    关注

    一、问题背景与技术挑战

    在现代富文本编辑器(如Quill、Slate.js)或内容管理系统中,将HTML片段转换为结构化JSON是常见需求。这种转换的核心目标是保留语义层级和格式信息,以便跨平台渲染或持久化存储。

    然而,用户输入的HTML往往存在标签重叠或非闭合情况,例如:

    <b><i>Hello</i></b><i>

    该片段中第二个<i>标签未闭合,且与前一个<i>形成“重叠”而非“嵌套”,直接使用正则或简单栈匹配会导致JSON结构错乱。

    典型错误表现为:子节点被错误地提升为兄弟节点,或闭合顺序混乱导致样式丢失。

    二、基础解析模型:基于栈的标签匹配机制

    最直观的解决方案是采用**栈结构**来模拟HTML标签的嵌套关系。每当遇到开标签时入栈,闭标签时出栈并构建对应JSON节点。

    • 初始化空栈与根JSON数组
    • 遍历解析后的token流(开标签、闭标签、文本)
    • 开标签 → 创建JSON对象并压入栈顶
    • 闭标签 → 弹出栈顶元素,并附加到父级children中

    示例代码如下:

    function htmlToJSON(tokens) {
        const stack = [];
        const root = { type: 'root', children: [] };
        let currentParent = root;
    
        tokens.forEach(token => {
            if (token.type === 'open') {
                const node = { type: 'element', tag: token.tag, children: [] };
                currentParent.children.push(node);
                stack.push(currentParent);
                currentParent = node;
            } else if (token.type === 'close') {
                if (stack.length) {
                    currentParent = stack.pop();
                }
            } else if (token.type === 'text') {
                currentParent.children.push({ type: 'text', value: token.value });
            }
        });
        return root;
    }

    三、进阶挑战:处理标签重叠与非闭合异常

    现实场景中,HTML常包含非法结构,如:

    输入HTML问题类型期望行为
    <b><i>A</b></i>标签交叉自动修正为合法嵌套
    <i><b>B</i>缺失闭合智能补全或截断
    <b><b>C</b>重复开启合并或分层处理

    这些问题若不处理,将导致JSON层级断裂或渲染错位。

    四、容错机制设计:智能闭合与栈修复策略

    为应对上述异常,需引入以下机制:

    1. 标签栈校验:当遇到闭标签但栈顶不匹配时,向上查找最近可匹配项
    2. 隐式闭合:对未闭合标签,在文档结束或新块级标签出现时强制闭合
    3. 优先级规则:定义标签嵌套优先级(如i允许嵌套于b),避免非法组合

    改进后的处理逻辑流程图如下:

    graph TD A[开始解析Token] --> B{Token类型?} B -- 开标签 --> C[创建节点,压栈] B -- 闭标签 --> D{栈顶是否匹配?} D -- 是 --> E[弹出栈,附加至父] D -- 否 --> F[查找最近匹配项] F --> G[中间节点强制闭合] G --> E B -- 文本 --> H[附加到当前父节点] E --> I[继续下一Token] H --> I I --> J{是否结束?} J -- 否 --> B J -- 是 --> K[剩余栈中节点隐式闭合] K --> L[输出JSON结构]

    五、实际应用中的扩展考量

    在真实系统中,还需考虑:

    • 属性继承:某些样式标签可能携带class或style属性,需在JSON中保留
    • 自闭合标签:如<br/>应直接生成leaf节点,不入栈
    • 块级/行内区分:块级元素(如div)影响布局,需特殊处理嵌套边界
    • 性能优化:大规模文档需流式处理,避免内存溢出

    此外,可结合AST(抽象语法树)工具如Cheerio或Parse5进行预清洗,再转入JSON生成阶段。

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

问题事件

  • 已采纳回答 12月5日
  • 创建了问题 12月4日