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

如何将嵌套HTML元素反序列化为Slate兼容的JSON结构?技术求助

解决嵌套HTML转Slate兼容JSON的问题

我明白你在把多层嵌套的HTML转换成Slate JSON结构时遇到了麻烦——像<u><em><strong>...</strong></em></u>这种叠加的格式,确实容易让默认的反序列化逻辑“卡壳”。我之前也踩过类似的坑,下面给你两个亲测有效的解决方案:

方案一:用slate-html-serializer库(推荐)

官方的slate-hyperscript更偏向于手动构建节点,而slate-html-serializer专门针对HTML与Slate的双向转换,处理嵌套格式更省心。

步骤1:安装依赖

npm install slate-html-serializer
# 或者用yarn
yarn add slate-html-serializer

步骤2:定义序列化规则并反序列化

你需要为每个HTML标签映射对应的Slate mark(因为你的内容都是文本格式,没有块级节点的变化):

import { HtmlSerializer } from 'slate-html-serializer';
import { Text } from 'slate';

// 定义HTML标签到Slate marks的映射
const rules = [
  // 处理粗体
  {
    deserialize(el, next) {
      if (el.tagName.toLowerCase() === 'strong') {
        return {
          object: 'text',
          leaves: next(el.childNodes).map(leaf => ({
            ...leaf,
            marks: [...(leaf.marks || []), { type: 'bold' }]
          }))
        };
      }
    }
  },
  // 处理斜体
  {
    deserialize(el, next) {
      if (el.tagName.toLowerCase() === 'em') {
        return {
          object: 'text',
          leaves: next(el.childNodes).map(leaf => ({
            ...leaf,
            marks: [...(leaf.marks || []), { type: 'italic' }]
          }))
        };
      }
    }
  },
  // 处理下划线
  {
    deserialize(el, next) {
      if (el.tagName.toLowerCase() === 'u') {
        return {
          object: 'text',
          leaves: next(el.childNodes).map(leaf => ({
            ...leaf,
            marks: [...(leaf.marks || []), { type: 'underline' }]
          }))
        };
      }
    }
  },
  // 处理普通文本节点
  {
    deserialize(el, next) {
      if (el.nodeType === 3) {
        return {
          object: 'text',
          leaves: [{ text: el.textContent, marks: [] }]
        };
      }
    }
  },
  // 处理p标签(块级节点)
  {
    deserialize(el, next) {
      if (el.tagName.toLowerCase() === 'p') {
        return {
          type: 'paragraph',
          children: next(el.childNodes)
        };
      }
    }
  }
];

const htmlSerializer = new HtmlSerializer({ rules });

// 你的示例HTML
const html = '<p><u><em><strong>Our S</strong></em></u><strong>ervi</strong><u><em><strong>ce</strong></em></u><em><strong>s </strong></em><em>A</em><u><em>And</em></u><u><em><strong> </strong></em></u>Mo<u><em><strong>More</strong></em></u></p>';

// 反序列化得到Slate JSON
const slateJson = htmlSerializer.deserialize(html);
console.log(slateJson);

转换后的结果示例

你会得到类似这样的Slate结构(简化版):

[
  {
    "type": "paragraph",
    "children": [
      {
        "text": "Our S",
        "marks": [{"type": "bold"}, {"type": "italic"}, {"type": "underline"}]
      },
      {
        "text": "ervi",
        "marks": [{"type": "bold"}]
      },
      {
        "text": "ce",
        "marks": [{"type": "bold"}, {"type": "italic"}, {"type": "underline"}]
      },
      // 剩下的文本节点按格式依次生成...
    ]
  }
]

方案二:手动遍历DOM节点(无额外依赖)

如果不想引入新库,可以直接用原生DOM API遍历节点,递归处理嵌套样式:

function htmlToSlate(html) {
  const parser = new DOMParser();
  const doc = parser.parseFromString(html, 'text/html');
  const pNode = doc.querySelector('p');

  function processNode(node, currentMarks = []) {
    if (node.nodeType === Node.TEXT_NODE) {
      if (!node.textContent.trim()) return [];
      return [{
        text: node.textContent,
        marks: [...currentMarks]
      }];
    }

    const tagName = node.tagName.toLowerCase();
    const newMarks = [...currentMarks];
    // 根据标签添加对应的mark
    if (tagName === 'strong') newMarks.push({ type: 'bold' });
    if (tagName === 'em') newMarks.push({ type: 'italic' });
    if (tagName === 'u') newMarks.push({ type: 'underline' });

    // 递归处理子节点
    return Array.from(node.childNodes).flatMap(child => processNode(child, newMarks));
  }

  return [
    {
      type: 'paragraph',
      children: processNode(pNode)
    }
  ];
}

// 使用示例
const slateJson = htmlToSlate(html);
console.log(slateJson);

这个方法的核心是把每个嵌套的标签转换成对应的mark,递归遍历文本节点时把当前的mark数组传递下去,最终生成Slate需要的文本节点结构。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 10:42:45