如何将嵌套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
相关产品推荐
相关产品推荐

