How OfficeCLI Template Merge Replaces {{key}} Placeholders in Office Documents
OfficeCLI's merge command uses a compiled regex pattern to locate {{key}} placeholders in DOCX, XLSX, and PPTX files, substituting them with values from a flattened JSON dictionary in a single pass while preserving document formatting.
The OfficeCLI tool from the iOfficeAI/OfficeCLI repository provides a cross-platform solution for programmatically populating Microsoft Office templates. According to the source code in CommandBuilder.Import.cs and Core/TemplateMerger.cs, the merge engine processes templates through a three-stage pipeline: JSON flattening, regex-based placeholder detection, and format-specific text replacement.
Command Entry Point and Argument Handling
The merge functionality is exposed through the CLI in CommandBuilder.Import.cs (lines 284‑313). This handler parses the template path, output destination, JSON data source (via --data), and the optional --force overwrite flag.
Once validated, the command invokes the core engine:
var data = Core.TemplateMerger.ParseMergeData(dataArg);
var mergeResult = Core.TemplateMerger.Merge(template, output, data, force);
This delegation separates CLI concerns from document manipulation logic, allowing the TemplateMerger class to operate independently of the command-line interface.
JSON Flattening and Data Preparation
Before replacement begins, TemplateMerger.ParseMergeData (lines 39‑89) normalizes input into a flat Dictionary<string,string>. This method accepts either an inline JSON string or a file path ending in .json.
The flattening algorithm executes two passes:
- Literal key preservation – Top-level keys containing dots (e.g.,
"a.b") are stored directly and take precedence over nested object structures. - Hierarchical flattening – Nested objects convert to dot-notation (
parent.child), while arrays use bracket-notation (items[0],items[1]).
This flattening ensures that a JSON structure like {"user": {"name": "Alice"}} becomes accessible via the key user.name in the template.
Placeholder Detection Regex
The engine identifies placeholders using a compiled regular expression defined in TemplateMerger.cs (line 28):
private static readonly Regex PlaceholderPattern =
new(@"\{\{\s*(\w[\w.\-\[\] ]*?)\s*\}\}", RegexOptions.Compiled);
This pattern tolerates:
- Outer whitespace inside the braces (
{{ key }}) - Dot-notation paths (
{{user.name}}) - Array indexing (
{{items[0]}}) - Hyphens and spaces within keys
The regex captures only the key identifier, excluding the surrounding braces, for direct dictionary lookups.
Format-Specific Replacement Strategies
TemplateMerger.Merge dispatches to specialized handlers based on the file extension, ensuring that each Office format's unique XML structure is manipulated correctly.
DOCX Handling
For Word documents (MergeDocx → ReplacePlaceholdersInDocx, lines 91‑126 and 168‑210), the engine:
- Opens the
WordprocessingDocumentand iterates through the main document part, headers, footers, footnotes, endnotes, and comments. - Targets all
<w:t>(text) elements containing the substring{{. - Invokes
SinglePassReplace, which executes the regex once per text node, substitutes matching keys with their values, and records usage statistics. - Performs single-pass substitution without feeding the result back into the regex, preventing nested placeholders from being interpreted as new keys.
- Runs
ScanUnresolvedDocxto collect any placeholders that remained after replacement.
This approach preserves Word's run formatting while ensuring that only intended placeholders are processed.
XLSX Handling
Excel spreadsheets are processed in MergeXlsx (lines 71‑108). The implementation:
- Opens the
SpreadsheetDocumentand walks every worksheet. - Extracts cell text, handling inline strings, shared strings, and plain value types.
- Applies
SinglePassReplaceto the extracted text. - Writes the modified value back using
SetCellText, converting the result to an inline string to avoid shared string table complications. - Scans all cells post-merge to identify unresolved placeholders.
This method maintains cell styles and data types while performing text substitution.
PPTX Handling
PowerPoint presentations require special handling for shape text (lines 89‑115). The MergePptx routine:
- Opens the
PresentationDocumentand iterates through slides and their notes pages. - Locates
<p:sp>(shape) elements containingTextBodyelements. - Concatenates all run texts within a shape to form the complete string for regex evaluation.
- Reconstructs the shape's text runs: the first run receives the entire replaced text, while subsequent runs are removed.
- Preserves the formatting properties (font, color, size) of the first run only.
This strategy ensures that placeholder replacement does not fragment text across multiple formatting spans.
Understanding the MergeResult Output
The Merge method returns a MergeResult object containing three critical properties:
ReplacedCount– Total number of successful substitutions across the entire document.UsedKeys– Collection of keys that actually appeared in the template.UnresolvedPlaceholders– Sorted list of placeholders that had no corresponding entry in the JSON data.
If unresolved placeholders exist, the CLI writes a warning to stderr, alerting users to potential data gaps while still producing the output file.
Usage Examples
Command-Line Usage
Merge a Word template with inline JSON:
officecli merge template.docx output.docx --data '{"name":"Alice","date":"2026-07-29"}'
Use an external JSON file and force overwrite:
officecli merge report.xlsx report-filled.xlsx --data ./data.json --force
Template and Data Structure
Template content (template.docx):
Dear {{ name }},
Your appointment is scheduled for {{ date }}.
Items: {{ items[0] }}, {{ items[1] }}
JSON data (data.json):
{
"name": "Alice",
"date": "2026-07-29",
"items": ["Laptop", "Monitor"]
}
Programmatic Usage (C#)
Integrate the merger directly into .NET applications:
using OfficeCli.Core;
// Parse JSON string or file path
var data = TemplateMerger.ParseMergeData("{\"title\":\"Q1 Report\",\"year\":\"2024\"}");
// Execute merge
var result = TemplateMerger.Merge(
template: "presentation.pptx",
output: "presentation-merged.pptx",
data: data,
force: true
);
Console.WriteLine($"Replacements made: {result.ReplacedCount}");
if (result.UnresolvedPlaceholders.Any())
{
Console.WriteLine($"Unresolved: {string.Join(", ", result.UnresolvedPlaceholders)}");
}
Summary
- OfficeCLI template merge operates through
CommandBuilder.Import.csandCore/TemplateMerger.csto process DOCX, XLSX, and PPTX files. - Input JSON is flattened into dot-notation and bracket-notation keys (e.g.,
user.name,items[0]) for uniform lookup. - A compiled regex
\{\{\s*(\w[\w.\-\[\] ]*?)\s*\}\}identifies placeholders with flexible whitespace and path syntax. - Single-pass replacement prevents nested placeholders from being recursively interpreted, ensuring predictable output.
- Format-specific handlers preserve document structure: Word runs remain intact, Excel cells retain styles, and PowerPoint shapes keep their first-run formatting.
- The
MergeResultobject reports replacement counts, used keys, and unresolved placeholders for auditability.
Frequently Asked Questions
How does OfficeCLI handle nested JSON objects in the data parameter?
OfficeCLI flattens nested objects into dot-notation keys during the ParseMergeData phase. For example, a JSON object {"company": {"name": "Acme"}} becomes accessible via the placeholder {{company.name}}. The flattening algorithm gives precedence to literal top-level keys containing dots over nested structures.
Can I use array indexes in my placeholders?
Yes. The regex pattern supports bracket notation for arrays. If your JSON contains "items": ["Alpha", "Beta"], you can reference these values using {{items[0]}} and {{items[1]}}. The flattening logic converts array elements into dictionary keys with bracket syntax.
Why are some placeholders left unresolved after merging?
Placeholders remain unresolved when the flattened JSON dictionary lacks a matching key. Common causes include typos in the template, missing data in the JSON source, or incorrect dot-notation paths. The CLI writes unresolved placeholder names to stderr and includes them in the MergeResult.UnresolvedPlaceholders list for debugging.
Does the merge process preserve formatting in Office documents?
Yes. The engine uses format-specific strategies to preserve styling: Word documents maintain run properties (bold, italics) on replaced text; Excel cells retain their existing styles and data types; PowerPoint shapes preserve the formatting of the first text run while removing subsequent runs to prevent fragmentation. The single-pass replacement ensures that formatting codes within the Office XML are not corrupted during text substitution.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →