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:

  1. Literal key preservation – Top-level keys containing dots (e.g., "a.b") are stored directly and take precedence over nested object structures.
  2. 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 (MergeDocxReplacePlaceholdersInDocx, lines 91‑126 and 168‑210), the engine:

  1. Opens the WordprocessingDocument and iterates through the main document part, headers, footers, footnotes, endnotes, and comments.
  2. Targets all <w:t> (text) elements containing the substring {{.
  3. Invokes SinglePassReplace, which executes the regex once per text node, substitutes matching keys with their values, and records usage statistics.
  4. Performs single-pass substitution without feeding the result back into the regex, preventing nested placeholders from being interpreted as new keys.
  5. Runs ScanUnresolvedDocx to 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:

  1. Opens the SpreadsheetDocument and walks every worksheet.
  2. Extracts cell text, handling inline strings, shared strings, and plain value types.
  3. Applies SinglePassReplace to the extracted text.
  4. Writes the modified value back using SetCellText, converting the result to an inline string to avoid shared string table complications.
  5. 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:

  1. Opens the PresentationDocument and iterates through slides and their notes pages.
  2. Locates <p:sp> (shape) elements containing TextBody elements.
  3. Concatenates all run texts within a shape to form the complete string for regex evaluation.
  4. Reconstructs the shape's text runs: the first run receives the entire replaced text, while subsequent runs are removed.
  5. 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.cs and Core/TemplateMerger.cs to 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 MergeResult object 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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →