CORE
描述符系统
声明式 XML 映射 — 描述符类型、构建器 API 和运行时函数
@office-open/core 的描述符系统提供 TypeScript Options 对象与 XML 之间的声明式双向映射。同一个描述符同时驱动序列化(Options → XML 字符串)和解析(Element → Options)。
描述符类型
ElementDescriptor
声明式属性/子元素映射,通过 DescriptorBuilder 构建:
import { element, stringify, parse } from "@office-open/core";
const spacingDesc = element<SpacingOptions>("w:spacing")
.attr("before", "w:before")
.attr("after", "w:after")
.build();
CustomDescriptor
用于复杂逻辑的自定义描述符,提供 stringify() 和 parse() 方法:
import type { CustomDescriptor } from "@office-open/core";
const myDesc: CustomDescriptor<MyOptions> = {
kind: "custom",
stringify(value, ctx) {
return `<w:my val="${value.name}"/>`;
},
parse(el, ctx) {
return { name: el.attributes?.["w:val"] } as Partial<MyOptions>;
},
};
DescriptorBuilder API
element() 函数创建一个流式构建器,支持链式调用:
const desc = element<MyOptions>("w:p")
.attr("alignment", "w:val")
.child("spacing", "w:spacing", spacingDesc)
.children("runs", "w:r", runDesc)
.text("text")
.build();
构建器方法
| 方法 | 说明 |
|---|---|
.attr(key, xmlName, opts?) | 属性映射 |
.child(key, tag, desc) | 单个子元素映射 |
.children(key, tag, desc) | 重复子元素映射(数组) |
.union(key, variants) | 多选一子元素映射 |
.text(key) | 文本内容映射 |
.custom(spec) | 自定义内容处理器 |
.build() | 构建不可变的描述符 |
attr 选项
.attr("size", "w:sz", {
default: 10, // 等于默认值时省略
encode: (v: number) => String(v * 2), // JS 值 → XML 字符串
decode: (raw: string) => Number(raw) / 2, // XML 字符串 → JS 值
})
运行时函数
stringify()
将 Options 对象序列化为 XML 字符串:
import { stringify } from "@office-open/core";
const xml = stringify(spacingDesc, { before: 240, after: 120 }, ctx);
// <w:spacing w:before="240" w:after="120"/>
当可选元素应被省略时返回 undefined。
parse()
将 XML Element 解析为 Options 对象:
import { parse } from "@office-open/core";
const opts = parse(spacingDesc, element, ctx);
// { before: 240, after: 120 }
上下文
WriteContext
序列化过程中传递的上下文对象:
interface WriteContext {
addRelationship(type: string, target: string, mode?: string): string;
addMedia(data: Uint8Array, type: string): string;
}
ReadContext
解析过程中传递的上下文对象:
interface ReadContext {
resolveRelationship(rId: string): string | undefined;
getPart(path: string): XmlElement | undefined;
getRaw(path: string): Uint8Array | undefined;
}
双向一致性
同一个描述符同时驱动序列化和解析,无需中间表示:
import { stringify, parse, element } from "@office-open/core";
const desc = element<FontOptions>("w:rPr")
.attr("bold", "w:b", { encode: boolEncode, decode: boolDecode })
.attr("size", "w:sz")
.build();
// 序列化
const xml = stringify(desc, { bold: true, size: 24 }, ctx);
// 解析
const parsed = parse(desc, element, ctx);
// parsed.bold === true, parsed.size === 24
序列化过程直接生成 XML 字符串,不创建中间对象树,以减少内存分配。
何时直接使用
通常不需要直接调用 stringify() 和 parse()。各格式包的顶层函数会自动处理:
generateDocument()/generatePresentation()/generateWorkbook()— 内部调用 stringifyparseDocument()/parsePresentation()/parseWorkbook()— 内部调用 parse
在以下场景直接使用描述符运行时:
- 构建自定义 OOXML 部件的描述符
- 调试序列化/解析结果
- 实现格式包的部件模块
OOXML 辅助函数
描述符内部使用的编码/解码辅助函数:
| 函数 | 说明 |
|---|---|
boolEncode | 将布尔值编码为 XML 属性值 |
boolDecode | 将 XML 属性值解码为布尔值 |
enumEncode | 将枚举值编码为 XML 字符串 |
enumDecode | 将 XML 字符串解码为枚举值 |