The JSON document model
The Rivoli PDF DOM serializes cleanly to and from JSON. Because the model is plain data, the JSON is the document: you can store it, version it, diff it, cache it, and reconstruct the exact same tree later. This page covers the serialization modes, why the output is deterministic, and how node identity survives the round trip.
The serialization helpers are extension methods in Rivoli.Pdf.Serialization.
Round-tripping a document
Section titled “Round-tripping a document”using Rivoli.Pdf;using Rivoli.Pdf.Serialization;
var doc = Document.Create("Annual Report", "Finance");doc.AddSection().Add(new Paragraph("Executive summary"));
string json = doc.ToJsonCanonical(); // Document -> JSONDocument copy = JsonSerialization.FromJson(json); // JSON -> DocumentFromJson reconstructs the full tree, including every node’s identity (see below).
There are file-based variants too: ToJsonFile(path), ToJsonFileCanonical(path),
ToJsonFileCompact(path), and FromJsonFile(path).
Three modes
Section titled “Three modes”All three modes preserve every bit of data: they differ only in formatting and size.
| Mode | Method | Best for |
|---|---|---|
| Default | ToJson() | General use, debugging |
| Canonical | ToJsonCanonical() | Version control, document comparison |
| Compact | ToJsonCompact() | Cache storage, network transmission |
var pretty = doc.ToJson(); // indented, camelCase, nulls omittedvar git = doc.ToJsonCanonical(); // deterministic, human-readablevar forCache = doc.ToJsonCompact(); // minified, ~30–50% smallerDefault mode
Section titled “Default mode”Indented, camelCase property names, with null values omitted. Good for reading and debugging.
Canonical mode
Section titled “Canonical mode”The format to commit to Git. It is deterministic: serializing the same document twice produces byte-identical output.
var a = doc.ToJsonCanonical();var b = doc.ToJsonCanonical();// a == b, alwaysDeterminism comes from a few rules:
- Properties are written in a stable order.
- Collections preserve insertion order.
- Enums serialize as consistent camelCase strings.
- Null values are omitted, removing a source of variation.
Byte-identical output means clean, reviewable diffs and reliable document-equality checks.
Compact mode
Section titled “Compact mode”Same data, no whitespace: typically 30–50% smaller. Use it for Redis/caches and network payloads, where humans aren’t reading the bytes. It is still deterministic.
What the JSON looks like
Section titled “What the JSON looks like”Block and inline types are distinguished by a $type discriminator, so the
polymorphic tree round-trips faithfully:
{ "id": "abc-123", "metadata": { "title": "Annual Report", "author": "Finance" }, "sections": [ { "id": "section-1", "blocks": [ { "$type": "paragraph", "id": "para-1", "text": "Content" } ] } ]}The discriminator values match the concrete types: paragraph, list, table,
imageBlock, pageBreak, containerBlock for blocks; run, hyperlink, span,
inlineImage, inlineSymbol for inlines.
Stable identity across the round trip
Section titled “Stable identity across the round trip”Every node carries an id (a GUID string), and that identity is preserved by
serialization. Serialize, deserialize, and each node keeps the same id: the tree
that comes back is identical to the one that went out, down to node identity.
var original = Document.Create("Doc");var paraId = ((Paragraph)original.AddSection().Add(new Paragraph("Hi")).Blocks[0]).Id;
var restored = JsonSerialization.FromJson(original.ToJsonCanonical());// the restored paragraph has the same Id as paraIdThis is what enables:
- Git-friendly history: a change to one paragraph shows up as a change to that paragraph’s object, not a wholesale rewrite, because everything else keeps its identity and order.
- Caching keyed on identity.
- Collaborative editing and diff/patch: two versions of “the same document” can be aligned node-by-node.
See Node identity for how identity is generated and why it’s immutable.
Why a JSON model at all?
Section titled “Why a JSON model at all?”Most PDF libraries treat the document as a write-once pipeline: you build it, you emit bytes, and the structure is gone. Rivoli PDF keeps the structured model as a first-class, serializable artifact. That unlocks server-side editing (load JSON, mutate the tree, save JSON), caching of expensive-to-build documents, deterministic comparison in tests, and a clean audit trail: all without re-deriving structure from rendered PDF.
- Change tracking: observing edits to the tree, which pairs naturally with a serialized model.
- The document model: the tree these bytes represent.