Descriptor System
All OOXML XML parts in @office-open/docx, @office-open/pptx, and @office-open/xlsx are defined as descriptors — plain objects that declare how to map between TypeScript options objects and XML. The same descriptor drives both directions: stringify (JSON → XML) and parse (XML → JSON).
Core Concepts
A descriptor describes the bidirectional mapping between a TypeScript options interface and its XML representation. Every descriptor is a CustomDescriptor<T> whose stringify() and parse() methods are hand-written for the part — there is no declarative attr/child builder. The same descriptor drives both directions: stringify (JSON → XML) and parse (XML → JSON).
Descriptors are consumed by two runtime functions: stringify(desc, value, ctx) and parse(desc, element, ctx).
CustomDescriptor
Each part exports its descriptor object with kind: "custom" and hand-written stringify/parse:
import type { CustomDescriptor } from "@office-open/core";
interface MyOptions {
items: string[];
separator?: string;
}
const myDesc: CustomDescriptor<MyOptions> = {
kind: "custom",
stringify(value, ctx) {
const parts = value.items.map((item) => `<w:item val="${item}"/>`).join("");
return `<w:container>${parts}</w:container>`;
},
parse(el, ctx) {
const items = (el.elements ?? [])
.filter((c) => c.type === "element" && c.name === "w:item")
.map((c) => c.attributes?.["w:val"] ?? "");
return { items } as MyOptions;
},
};
Advanced: When a descriptor's stringify input differs from its parse output (for example, an accumulator-style input versus a structured parse result), declare both explicitly:
CustomDescriptor<TInput, Ctx, TOutput>.TOutputdefaults toTInput, so the common single-parameter formCustomDescriptor<T>is shorthand forCustomDescriptor<T, WriteContext, T>.
Runtime Functions
The runtime is a single step in each direction — no intermediate representation:
import { stringify, parse } from "@office-open/core";
stringify(desc, value, ctx)
Serialize an options object to an XML string. Returns undefined when an optional element should be omitted.
const xml = stringify(myDesc, { items: ["a"] }, writeCtx);
parse(desc, element, ctx)
Parse an XML element into an options object.
const result = parse(myDesc, element, readCtx);
XSD Value Mappings
When an XSD type uses abbreviations (e.g. ST_TextAlignType "ctr" for center), the bidirectional mapping lives in @office-open/core (util/mappings.ts). Each mapping is built once with bidi() and exposes .to() (user value → XSD value) and .from() (XSD value → user value), so the same map serves both stringify and parse:
import { xsdTextAlign } from "@office-open/core";
// stringify (Options → XML)
attrs.push(`algn="${xsdTextAlign.to(options.alignment)}"`); // "center" → "ctr"
// parse (XML → Options)
result.alignment = xsdTextAlign.from(String(el.attributes["algn"])); // "ctr" → "center"
When the XSD already uses full English words (e.g. "start", "center"), no mapping is needed — the value is written and read verbatim.
Context Objects
WriteContext
Passed during stringify (write path):
interface WriteContext {
addRelationship(type: string, target: string, mode?: string): string;
addMedia(data: Uint8Array, type: string): string;
}
ReadContext
Passed during parse (read path):
interface ReadContext {
resolveRelationship(rId: string): string | undefined;
getPart(path: string): XmlElement | undefined;
getRaw(path: string): Uint8Array | undefined;
}
When to Use
You typically don't need to call stringify() and parse() directly — the format packages' top-level functions handle everything internally:
generateDocument()/generatePresentation()/generateWorkbook()— call stringify internallyparseDocument()/parsePresentation()/parseWorkbook()— call parse internally
Use the descriptor system directly when you need to:
- Define custom OOXML elements not covered by the format packages
- Build format-specific extensions or plugins
- Implement bidirectional (stringify + parse) support for new XML parts
Naming Conventions
| Convention | Example |
|---|---|
Descriptor names: <part>Desc | spacingDesc, settingsDesc, slideDesc |
Options interfaces: <Part>Options | WorkbookOptions, DocumentOptions |
Helper functions: stringify*() / parse*() | stringifyWorksheet(), parseWorkbook() |