# How OfficeCLI Template Merge Replaces {{key}} Placeholders in Office Documents

> Learn how OfficeCLI's template merge replaces {{key}} placeholders in Office documents. Discover its efficient regex and JSON-based approach to data insertion.

- Repository: [OfficeAI/OfficeCLI](https://github.com/iofficeai/OfficeCLI)
- Tags: how-to-guide
- Published: 2026-07-31

---

**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`](https://github.com/iOfficeAI/OfficeCLI/blob/main/CommandBuilder.Import.cs) and [`Core/TemplateMerger.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/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`](https://github.com/iOfficeAI/OfficeCLI/blob/main/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:

```csharp
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`](https://github.com/iOfficeAI/OfficeCLI/blob/main/TemplateMerger.cs) (line 28):

```csharp
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:

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:

```bash
officecli merge template.docx output.docx --data '{"name":"Alice","date":"2026-07-29"}'

```

Use an external JSON file and force overwrite:

```bash
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):**

```json
{
  "name": "Alice",
  "date": "2026-07-29",
  "items": ["Laptop", "Monitor"]
}

```

### Programmatic Usage (C#)

Integrate the merger directly into .NET applications:

```csharp
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`](https://github.com/iOfficeAI/OfficeCLI/blob/main/CommandBuilder.Import.cs) and [`Core/TemplateMerger.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/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.