首页
/ Docling 序列化体系详解:从 DoclingDocument 多格式导出到自定义 Serializer

Docling 序列化体系详解:从 DoclingDocument 多格式导出到自定义 Serializer

2026-09-04 23:45:58作者:舒璇辛Bertina

Docling(Get your documents ready for gen AI)在完成 PDF、DOCX 等文档解析后,会产出统一的结构化中间表示 DoclingDocument。而如何把这份中间表示落回 Markdown、HTML、DocTags、JSON 等文本形态,则由一套独立的序列化(serialization)抽象承担。本文基于仓库文档 docs/concepts/serialization.md 及其配套示例 docs/examples/serialization.ipynb,系统讲解 Docling 序列化器的抽象层次、DoclingDocument 导出方法、各格式对表格跨单元格(span)的处理差异,以及如何配置和编写自定义 serializer,帮助你在 RAG、批量转换、文档入库等场景中精确控制导出结果。

1. 序列化抽象:从文档级到组件级

Docling 把「把文档变成文本」这件事拆成了三层抽象:

  1. 文档序列化器(document serializer):以一个 DoclingDocument 实例初始化,负责产出整篇文档的文本表示(textual representation)。这是最常用的入口;
  2. 组件序列化器(component serializers):面向文档的子构件,例如 text serializertable serializerpicture serializerlist serializerinline serializer 等。文档级序列化器内部会按组件类型委派给对应的组件序列化器;
  3. 序列化器提供者(serializer provider):进一步把「序列化策略」与「文档实例」解耦的包装层,便于下游应用统一替换序列化行为。

这一分层直接决定了后文的两个能力:你可以只替换某一种组件(比如把 Markdown 输出的表格换成 triplet 形式),也可以整体替换文档级序列化器,而不必触碰转换管线本身。

2. 基类体系与 serialize() 契约

为了兼顾下游应用的灵活性与开箱即用的便利,Docling 定义了一组序列化类层次(实现位于 docling-core 依赖包中,本仓库 pyproject.toml 声明其版本约束为 docling-core>=2.91.0,<3.0.0):

  • 各抽象的基类:BaseDocSerializerBaseTextSerializerBaseTableSerializer 等组件基类,以及 BaseSerializerProvider
  • 上述基类之外的具体实现子类,例如 MarkdownDocSerializerHTMLDocSerializer

从客户端视角看,最核心的契约是 BaseDocSerializer.serialize():它返回该文档的文本表示,同时附带哪些文档组件实际参与(贡献)了这次序列化的元数据。这一点在做导出审计或调试「某段文本为什么没出现」时非常有用——你可以通过序列化结果中的 span 来源信息定位到具体组件。

3. DoclingDocument 的导出方法:序列化器的用户级快捷方式

Docling 预置了 Markdown、HTML、DocTags 等序列化器,并在 DoclingDocument 上以导出方法的形式直接暴露。文档说明得很明确:像 export_to_markdown() 这样的导出方法本质上是用户快捷方式(user shorthands),其内部就是直接实例化并委派给相应的 serializer。

在当前仓库中,可以观察到以下导出方法的真实使用分布(对 docling/tests/ 下 Python 代码的统计):

导出方法 使用频次(仓库内) 典型场景
export_to_markdown() 138 次 绝大多数示例、CLI、测试的默认输出
export_to_indented_text() 16 次 纯文本/缩进结构输出(.itxt 地面真值校验)
export_to_dict() 9 次 稳定 schema 的结构化导出
export_to_html() 4 次 保留表格 rowspan/colspan 等结构
export_to_doclang() / export_to_doctags() 各 2 / 1 次 Doclang 文本、DocTags 训练格式

例如 docs/examples/batch_convert.py 展示了批量转换时按目标格式分发导出:YAML 落地 export_to_dict() 结果、.doctags 文件用 export_to_doctags()、Markdown 则同时演示了 export_to_markdown()export_to_markdown(strict_text=True) 两种形态;docs/examples/custom_convert.py 也以同样方式把 JSON、Markdown、DocTags 三种导出写入独立文件。而 docling/datamodel/document.py 在需要「保证稳定 schema」的场景下显式使用 export_to_dict() 做文档序列化,注释写明了其动机——这正是选择 dict 导出而非文本导出的一个代表性理由。

DoclingDocument 本体(及其全部 export_to_* 方法)定义在 docling-core 包中,本仓库通过 docling/datamodel/document.py 重新导出 DoclingDocument 等类型,方便用户统一从 docling.datamodel.document 引用。

4. 格式特定行为:表格单元格跨行跨列(span)如何处理

这是文档中最重要的「格式权衡」章节。Docling 的内部表格模型(TableData.grid)为每个单元格保留了完整的 span 元数据:row_spancol_spanstart_row_offset_idxstart_col_offset_idx。不同输出格式对这些元数据的渲染策略如下(完整继承自原文档):

格式 Span 处理
JSON 保留。完整的 TableData 模型无损序列化,包含全部 span 字段。
Doclang 保留。表格通过 OTSL 序列化,使用显式续格 token:跨列用 LCEL、跨行用 UCEL、两者兼有则 XCEL
DocTags 保留。表格经 OTSL 序列化,OTSL 天然编码 span 结构。
HTML 保留。单元格直接输出原生 rowspan / colspan 属性。
Markdown 拍平(Flattened)。Markdown 表格没有 span 语法,序列化器只在原点位置写入单元格文本,span 覆盖的其他网格位置渲染为空单元格。
LaTeX 拍平tabular 环境暂不输出 \multirow / \multicolumn 命令。
WebVTT 不适用。WebVTT 是字幕/说明格式,表格不会被序列化。

实践结论:如果下游流程依赖准确的表格结构(例如合并的表头单元格),应优先使用 export_to_html()export_to_dict(),而不是 export_to_markdown()。当然,如果你确实需要 Markdown 中承载跨格信息,也可以子类化 BaseTableSerializer 实现自定义逻辑,并在实例化文档序列化器时传入(见第 7 节的配置示例,table_serializer 参数正是为这种替换设计的)。

5. 实战一:直接应用预置 Serializer

以下代码整理自 docs/examples/serialization.ipynb,演示「转换 + 序列化」的完整闭环。先转换得到 DoclingDocument,再对其施加任意 BaseDocSerializer

from docling.document_converter import DocumentConverter

DOC_SOURCE = "https://arxiv.org/pdf/2311.18481"
converter = DocumentConverter()
doc = converter.convert(source=DOC_SOURCE).document

HTML 序列化——表格会以 <table> 结构输出(span 完整保留),图片以 <figure>/<figcaption> 呈现:

from docling_core.transforms.serializer.html import HTMLDocSerializer

serializer = HTMLDocSerializer(doc=doc)
ser_result = serializer.serialize()
ser_text = ser_result.text   # 序列化结果同时携带组件贡献元数据

Markdown 序列化——表格转为标准管道表格(span 被拍平),图片按 MarkdownParams 配置输出占位符(默认 <!-- image -->)或引用/嵌入形式:

from docling_core.transforms.serializer.markdown import MarkdownDocSerializer

serializer = MarkdownDocSerializer(doc=doc)
ser_text = serializer.serialize().text

6. 实战二:配置 Serializer——替换表格序列化器与参数

同一 Notebook 演示了如何「重配置」Markdown 序列化,满足两类诉求:

  • 使用不同的组件序列化器:例如把表格输出改为 triplet 形式(行1, 列A = 值1. 行1, 列B = 值2. ...),笔记指出这种扁平键值对形式有助于向量检索场景下的表格表示;
  • 使用用户自定义参数:例如替换默认的图片占位符文本。
from docling_core.transforms.chunker.hierarchical_chunker import TripletTableSerializer
from docling_core.transforms.serializer.markdown import MarkdownParams

serializer = MarkdownDocSerializer(
    doc=doc,
    table_serializer=TripletTableSerializer(),   # 替换组件级序列化器
    params=MarkdownParams(
        image_placeholder="<!-- demo picture placeholder -->",
        # ... 其余 MarkdownParams 参数按需提供
    ),
)
ser_text = serializer.serialize().text

从 Notebook 的实际输出对比可以验证两处差异:同一张 IBM/Starbucks ESG 表格,默认配置下是 | Report | Question | Answer | 管道表格;换成 TripletTableSerializer 后变为 IBM 2022, Question = ...?. IBM 2022, Answer = ... 的连续文本串;同时图片占位符也从默认的 <!-- image --> 变成了配置中的 <!-- demo picture placeholder -->。这直接印证了第 1 节所说的组件级替换机制:只动一个子序列化器,文档其余部分的输出完全不受影响。

7. 实战三:编写自定义 Serializer

当现有实现都不满足时,你可以定义自定义序列化逻辑。Notebook 给出的例子是:让图片序列化额外带上 picture description(图像描述注解)。前提是转换管线开启了 picture description enrichment(通过 PdfPipelineOptions(do_picture_description=True, picture_description_options=PictureDescriptionVlmOptions(...), generate_picture_images=True, images_scale=2),并配合 PictureDescriptionVlmOptions 指定 VLM 模型与 prompt)。

自定义组件序列化器——继承 MarkdownPictureSerializer 并重写 serialize(),先调用父类拿到基础输出,再追加注解:

from docling_core.transforms.serializer.base import BaseDocSerializer, SerializationResult
from docling_core.transforms.serializer.common import create_ser_result
from docling_core.transforms.serializer.markdown import MarkdownPictureSerializer
from docling_core.types.doc.document import DoclingDocument, PictureItem


class AnnotationPictureSerializer(MarkdownPictureSerializer):
    def serialize(self, *, item, doc_serializer, doc, separator=None, **kwargs):
        text_parts = []
        # 复用父类结果(占位符/图片引用部分)
        parent_res = super().serialize(item=item, doc_serializer=doc_serializer, doc=doc, **kwargs)
        text_parts.append(parent_res.text)
        # 追加 picture description 注解
        if item.meta is not None and item.meta.description is not None:
            text_parts.append(f"<!-- Picture description: {item.meta.description.text} -->")
        text_res = (separator or "\n").join(text_parts)
        return create_ser_result(text=text_res, span_source=item)

注意 create_ser_result(text=..., span_source=item):它把序列出的文本与源组件(item)绑定,形成 serialize() 契约中「哪些组件贡献了输出」的元数据。然后把自定义图片序列化器挂到文档级序列化器上:

serializer = MarkdownDocSerializer(
    doc=doc,
    picture_serializer=AnnotationPictureSerializer(),
    params=MarkdownParams(image_mode=ImageRefMode.PLACEHOLDER, image_placeholder=""),
)
ser_text = serializer.serialize().text

运行后,Markdown 输出中每个图片占位处都会带上一条 <!-- Picture description: ... --> 注释,内容即管线阶段由 VLM 生成的图片描述。

Notebook 还覆盖了另一个常见诉求——为每张图片生成唯一标识以便下游与原始 DoclingDocument 匹配:自定义 _serialize_image_part(),从 item.self_ref 解析出图片索引,并对 image_placeholder 中的 {index} 占位 token 做替换,配合 params=MarkdownParams(image_mode=ImageRefMode.PLACEHOLDER, image_placeholder="<!-- image_{index} -->"),即可让每张图在导出文本中拥有形如 <!-- image_2 --> 的独立编号。

8. 小结与延伸阅读

  • 序列化是 Docling 转换管线之后的独立层:DoclingDocumentexport_to_* 方法只是预置 serializer 的快捷封装,需要精细控制时直接实例化 MarkdownDocSerializer / HTMLDocSerializer 等并传入组件级序列化器与 Params
  • 选择导出格式的核心判据之一是表格 span 保真度:JSON/Doclang/DocTags/HTML 保留完整结构,Markdown/LaTeX 当前会拍平;
  • 组件级 serializer 可任意替换(table、picture、list……),文档级策略可整体替换,扩展点是子类化而非修改管线。

进一步阅读:docs/concepts/docling_document.mdDoclingDocument 数据结构)、docs/examples/serialization.ipynb(完整可运行示例,含上述全部代码)、docs/examples/batch_convert.pydocs/examples/custom_convert.py(批量/自定义转换中的多格式导出落地)、docs/examples/export_figures.pydocs/examples/export_tables.py(图片与表格的独立导出)。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
889
5.78 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
981
502
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384