# 读取、替换与校验内容

分清插入文字、替换正文和打开另一份文档，并在解析前保留原始数据。

拿到服务端数据后，先确定它属于哪一种文档模型，再把内容交给 Editor。HTML、JSON 和 Markdown 是内容表示方式；它们不会替你安装对应的节点、格式或转换规则。

## 选择内容表示

| 内容 | 如何使用 | 需要注意 |
| --- | --- | --- |
| JSON | `content: { type: 'doc', content: [...] }` | 适合保存产品自己的结构，恢复时使用相容的 Schema |
| HTML | `content: '<p>Hello</p>'` | 字符串默认按 HTML 解析；需要 DOM 环境，解析规则由扩展提供 |
| Markdown | `content: '# Hello', contentType: 'markdown'` | 显式指定类型；只有已安装扩展提供的 Markdown 规则能参与转换 |

`getJSON()` 读取规范结构，`getText()` 读取纯文本，`getHTML()` 使用当前 Schema 的 HTML 序列化器，`getMarkdown()` 使用 Markdown 转换器。HTML 输出需要浏览器 DOM；它不导出分页画布、产品工具栏或页面截图。自定义节点应提供所需格式的转换规则，不能只检查文字看起来相同。

## 先分清要做哪一种修改

| 任务 | 调用 | 结果 |
| --- | --- | --- |
| 在当前选区加入内容 | `commands.insertContent(content)` | 插入或替换选区，进入普通编辑历史 |
| 替换当前文档的正文 | `commands.setContent(content, options)` | 替换子内容，保留当前文档顶层属性 |
| 打开完整文档 | `commands.replaceDocument(content, options)` | 同时恢复顶层属性，选区回到开头，本次替换不加入历史 |

Word 的文档设置、样式注册表等信息保存在根节点属性中，分节则由正文中的 section 节点表达，因此文件恢复不能只调用 `setContent()`。`replaceDocument()` 的“不加入历史”也不等于建立全新的编辑会话；切换业务文档时，应用仍需隔离旧任务、订阅和历史策略。需要明确的新会话边界时，创建新的 Editor；Classic 可使用[替换文档配置](/classic/files)。

```ts [替换经过校验的完整文档]
const accepted = editor.commands.replaceDocument(content, {
  errorOnInvalidContent: true,
  emitUpdate: false,
})
```

`errorOnInvalidContent: true` 使解析失败抛出错误，调用方需要捕获。`emitUpdate: false` 只抑制内容更新通知；文档仍被替换，事务仍会发生。这适合应用自己建立已保存基线的恢复流程，不是静默执行所有副作用的开关。

## 校验发生在替换之前

完整备份至少检查三件事：应用的模型与版本、节点/标记/属性名称、Schema 允许的内容结构。解析后的节点可以调用 `check()` 检查结构，但解析器可能已经忽略未知属性，因此只做 `schema.nodeFromJSON(value).check()` 不能证明原文件没有丢字段。

保留原始输入，在转换前拒绝未知字段或执行明确的迁移。完整示例的 `files/backup.ts` 先检查名称，再解析、检查根节点与子内容；实现见[文件操作](/build/files)。失败时保留当前草稿，不要把回退得到的空白文档重新保存到原位置。

创建选项 `enableContentCheck` / `emitContentError` 可以报告初始内容错误；当前构造流程仍可能建立回退内容，不能把“构造没有抛错”当作导入成功。严格恢复应先验证，再提交。

## 内容更新与保存状态

`setEditable()` 默认也会发出 `update`，即使正文没有变化。记录未保存修改时比较前后文档，而不是把每个 `update` 都当成一次正文编辑；示例见[生命周期与事件](/concepts/lifecycle)。保存请求完成后也要确认当前文档仍是提交的那一版，见[保存与恢复](/guide/persistence)。

参数的完整定义见 [Editor 配置与事件参考](/api/options)。
