# How the OfficeCLI Template Merge Command Replaces {{key}} Placeholders in DOCX, XLSX, and PPTX Files

> Learn how the OfficeCLI merge command efficiently replaces {{key}} placeholders in DOCX, XLSX, and PPTX files using a compiled regex engine preserving document formatting.

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

---

**The OfficeCLI `merge` command uses a compiled regex pattern and single-pass substitution engine in [`Core/TemplateMerger.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/Core/TemplateMerger.cs) to replace `{{key}}` placeholders with flattened JSON values across Word, Excel, and PowerPoint files while preserving document formatting and reporting unresolved tokens.**

The OfficeCLI template merge command provides a programmatic way to populate Microsoft Office documents using JSON data sources. Implemented in the iOfficeAI/OfficeCLI repository, the tool parses structured data, detects placeholder tokens using flexible pattern matching, and executes format-specific replacements across DOCX, XLSX, and PPTX files without altering underlying document styles.

## Command Flow and Architecture

In [`CommandBuilder.Import.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/CommandBuilder.Import.cs) (lines 284‑313), the CLI defines the `merge` command and delegates document processing to [`Core/TemplateMerger.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/Core/TemplateMerger.cs). The entry point first parses the JSON data argument, then invokes the merge engine:

```csharp
var data = Core.TemplateMerger.ParseMergeData(dataArg);
var mergeResult = Core.TemplateMerger.Merge(template, output, data, force);

```

This two-phase approach separates argument handling from file manipulation, allowing the `TemplateMerger` class to focus exclusively on document traversal and text substitution.

## JSON Data Processing and Flattening

Before reaching the document layers, `TemplateMerger.ParseMergeData` (lines 39‑89) normalizes input into a flat `Dictionary<string,string>`. The method accepts either inline JSON strings or file paths, then executes a two-pass flattening algorithm:

- **Pass 1** stores literal top-level keys directly, ensuring explicit dot-notation keys like `"a.b"` take precedence over nested objects.
- **Pass 2** flattens nested objects into dot-notation paths (`parent.child`) and arrays into bracket indices (`items[0]`).

This flattening strategy enables templates to reference nested JSON values using intuitive `{{parent.child}}` or `{{items[0]}}` syntax.

## Placeholder Detection Regex

The engine identifies placeholders using a compiled regular expression defined at line 28 of [`TemplateMerger.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/TemplateMerger.cs):

```csharp
private static readonly Regex PlaceholderPattern =
    new(@"\{\{\s*(\w[\w.\-\[\] ]*?)\s*\}\}", RegexOptions.Compiled);

```

This pattern tolerates outer whitespace, hyphens, inner spaces, dot-path navigation, and array indexes, matching tokens like `{{ name }}`, `{{user.email}}`, or `{{items[0].title}}`.

## Format-Specific Merge Implementation

`TemplateMerger.Merge` copies the template to the output path, then dispatches to format-specific handlers based on file extension. Each handler preserves the host application's formatting while executing replacements.

### DOCX Handling (Word Documents)

The `MergeDocx` method processes WordprocessingDocument parts, enumerating the main document body, headers, footers, footnotes, endnotes, and comments. For every `<w:t>` text element containing `{{`, it calls `SinglePassReplace` (lines 130‑166):

- Runs the regex once per text element to prevent nested placeholder expansion.
- Substitutes matching keys with dictionary values.
- Records used keys and replacement counts without feeding results back into the regex.

After replacement, `ScanUnresolvedDocx` (lines 168‑210) performs a second pass to collect any remaining placeholders for the final report.

### XLSX Handling (Excel Workbooks)

For Excel files, `MergeXlsx` opens the SpreadsheetDocument and iterates through every worksheet cell. The implementation:

- Extracts cell text handling inline strings, shared strings, and plain strings.
- Calls `SinglePassReplace` on the extracted value.
- Writes the result back using `SetCellText` as an inline string to preserve formatting.

A final scan identifies unresolved placeholders across all cells.

### PPTX Handling (PowerPoint Presentations)

The `MergePptx` method processes PresentationDocument slides and notes. For each `<p:sp>` shape containing a TextBody:

- Concatenates all run texts from the shape.
- Executes `SinglePassReplace` on the combined string.
- Re-writes the run collection: the first run receives the new text while subsequent runs are removed, preserving the original formatting of the first run.

This approach avoids breaking PowerPoint's run-based styling while ensuring placeholder text receives complete replacement.

## Merge Results and Reporting

Each merge operation returns a `MergeResult` object containing:

- **ReplacedCount**: Total successful substitutions across the document.
- **UsedKeys**: Dictionary keys that actually appeared in the template.
- **UnresolvedPlaceholders**: Sorted list of placeholders that remained after substitution.

The CLI prints a summary to stdout and writes warnings to stderr when unresolved placeholders exist, enabling automated validation of template completeness.

## Practical Usage Examples

Perform a basic merge using inline JSON:

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

```

Use a JSON file with the force flag to overwrite existing outputs:

```bash
officecli merge report.xlsx report-out.xlsx --data data.json --force

```

Programmatically invoke the merger from C#:

```csharp
var data = OfficeCli.Core.TemplateMerger.ParseMergeData("{\"title\":\"Q1 Report\",\"year\":2024}");
var result = OfficeCli.Core.TemplateMerger.Merge("report.pptx", "report-merged.pptx", data, force: true);

Console.WriteLine($"Replacements: {result.ReplacedCount}");
if (result.UnresolvedPlaceholders.Any())
    Console.WriteLine("Unresolved: " + string.Join(", ", result.UnresolvedPlaceholders));

```

## Summary

- The OfficeCLI `merge` command implements a **single-pass regex substitution** engine in [`Core/TemplateMerger.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/Core/TemplateMerger.cs) that processes DOCX, XLSX, and PPTX files uniformly.
- **JSON data flattening** supports dot-notation and bracket-index syntax for accessing nested values and array elements.
- **Format-specific handlers** preserve document structure: Word text elements, Excel cell strings, and PowerPoint shape runs retain their original styling after replacement.
- The system reports **replacement metrics and unresolved placeholders**, enabling template validation and debugging.
- All operations occur through the Open XML SDK, ensuring compatibility with Microsoft Office formats without requiring the Office applications to be installed.

## Frequently Asked Questions

### How does OfficeCLI handle nested JSON objects in templates?

OfficeCLI flattens nested JSON objects into dot-notation keys during the parsing phase in `ParseMergeData`. For example, a JSON object `{"user": {"name": "Alice"}}` becomes accessible via `{{user.name}}`. Arrays are flattened using bracket notation (`items[0]`), allowing templates to reference specific indices.

### What happens if a placeholder in the template doesn't exist in the JSON data?

Unresolved placeholders remain in the document text unchanged. After processing, the `MergeResult` object contains a sorted list of `UnresolvedPlaceholders`, and the CLI writes a warning to stderr. This allows you to identify missing data keys without breaking the document generation process.

### Does OfficeCLI support recursive placeholder replacement within replaced values?

No. The `SinglePassReplace` method executes the regex exactly once per text segment and does not feed replacement results back into the regex engine. This design prevents accidental recursive expansion and ensures that literal text containing curly braces in your JSON values remains unchanged.

### Can I use the merge functionality programmatically without the CLI?

Yes. The `TemplateMerger` class in [`src/officecli/Core/TemplateMerger.cs`](https://github.com/iOfficeAI/OfficeCLI/blob/main/src/officecli/Core/TemplateMerger.cs) exposes public static methods including `ParseMergeData` and `Merge` that you can call directly from C# code. This allows integration into .NET applications, automated build pipelines, or custom document generation services without spawning command-line processes.