OpenDocx.NET 1.1.8

dotnet add package OpenDocx.NET --version 1.1.8
                    
NuGet\Install-Package OpenDocx.NET -Version 1.1.8
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="OpenDocx.NET" Version="1.1.8" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="OpenDocx.NET" Version="1.1.8" />
                    
Directory.Packages.props
<PackageReference Include="OpenDocx.NET" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add OpenDocx.NET --version 1.1.8
                    
#r "nuget: OpenDocx.NET, 1.1.8"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package OpenDocx.NET@1.1.8
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=OpenDocx.NET&version=1.1.8
                    
Install as a Cake Addin
#tool nuget:?package=OpenDocx.NET&version=1.1.8
                    
Install as a Cake Tool

OpenDocx.NET

OpenDocx.NET translates Word templates containing text-delimited fields into OpenXmlPowerTools templates, so applications can assemble documents using their own expression language and data model.

Authors work in Word and write fields such as {[Customer.Name]}, {[if IncludeDetails]}, and {[list Items]}. OpenDocx identifies those fields, checks their structure, and compiles the DOCX into the form expected by OpenXmlPowerTools (OXPT). Your application evaluates the expressions; OXPT handles the document assembly.

OpenDocx is a .NET library for document-generation applications. Its main integration point is the boundary between a Word template and the application that supplies its values.

How it fits together

There are two distinct stages:

  1. Provision a template, usually when an author uploads or changes it. Normalize the source DOCX, extract its fields, parse conditional/list structure, and compile an OXPT-compatible DOCX. Save the compiled template together with its field dictionary and logic tree.
  2. Assemble a document, whenever data is available. Evaluate the expressions in the appropriate conditional and list scopes, construct the XML data expected by the compiled template, and pass both to the assembler.
source.docx with {[fields]}
        |
        | normalize, extract, parse, compile
        v
compiled oxpt.docx + field dictionary / logic tree
        |                          |
        |                 your expression evaluator + application data
        |                          |
        |                          v
        +-------------------- evaluated XML
                                   |
                                   | OXPT document assembly
                                   v
                             assembled.docx

The compiled DOCX contains OXPT content controls and selectors. Expressions from the source template are mapped to generated names such as C1 or L4, called atoms in the API. Those names connect the field metadata, evaluated XML, and compiled template. Keep all three associated with the same template version.

This separation lets a host application implement an expression language in .NET, JavaScript, or another environment. The host can validate expressions against its own schema, retrieve data, apply business rules, and format values before assembly. OpenDocx parses the template's control structure; it does not execute the expressions or fetch application data. JSON metadata describes the work for the host; the assembly APIs shown here consume evaluated XML.

Authoring templates

Type fields as ordinary text in a DOCX document. For example:

Dear {[Customer.Name]},

{[if IncludeDetails]}
Your reference is {[Reference]}.
{[elseif IncludeSummary]}
A summary will follow.
{[else]}
Please contact us for details.
{[endif]}

{[list Items]}
{[Description]}: {[FormattedAmount]}
{[endlist]}

if / elseif / else / endif and list / endlist establish the template's structure; the remaining field text is an expression for the host to interpret. Conditionals and lists can nest. Custom text delimiters are also supported.

Placement in Word is significant. Paired fields within a paragraph must match within that paragraph; block-level markers can enclose multiple paragraphs. Starting with 1.1.8, any authored whitespace around a content field makes it run-level. Existing whitespace treatment for control markers is retained. Floating pictures and text boxes must have supported anchors; normalization reports unsupported anchors instead of silently losing them during assembly. See the changelog for release-specific behavior.

A minimal C# example

Create greeting.docx in Word containing one paragraph:

Hello {[Customer.Name]}.

The following console-program code provisions that template and assembles greeting-assembled.docx. Its evaluator is deliberately a single explicit lookup; a real host would supply its own expression evaluator.

using System;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Xml.Linq;
using OpenDocx;

// Provision once for this version of the template.
var source = File.ReadAllBytes("greeting.docx");
var normalized = Normalizer.NormalizeTemplate(source);
var parsed = Templater.ParseFields(normalized.ExtractedFields);
var compiled = Templater.CompileTemplate(
    normalized.NormalizedTemplate, parsed.ParsedFields);
if (compiled.HasErrors)
    throw new InvalidOperationException(string.Join("\n", compiled.Errors));

var jsonOptions = OpenDocx.OpenDocx.DefaultJsonOptions;
File.WriteAllBytes("greeting.oxpt.docx", compiled.Bytes);
File.WriteAllText("greeting.fields.json",
    JsonSerializer.Serialize(parsed.ParsedFields, jsonOptions));
File.WriteAllText("greeting.logic.json",
    JsonSerializer.Serialize(parsed.LogicTree, jsonOptions));

// At assembly time, evaluate the expressions using application data.
// This example has content fields only, so no scope traversal is needed.
string Evaluate(string expression) => expression switch
{
    "Customer.Name" => "Ada Lovelace",
    _ => throw new InvalidOperationException($"Unknown expression: {expression}")
};

var data = new XElement("_odx",
    parsed.ParsedFields.Values
        .Where(field => field.Type == FieldType.Content)
        .GroupBy(field => field.Atom)
        .Select(group => new XElement(group.Key, Evaluate(group.First().Expression))));

var assembled = Assembler.AssembleDocument(compiled.Bytes, data);
if (assembled.HasErrors)
    throw new InvalidOperationException(assembled.Error);
File.WriteAllBytes("greeting-assembled.docx", assembled.Bytes);

The output reads Hello Ada Lovelace. with the template's formatting. The example uses returned atoms rather than assuming a field number. NormalizeTemplate rejects unsupported placements, and ParseFields throws FieldParseException for structural errors such as unmatched markers. Use Normalizer.AnalyzeTemplate when you need structured normalization diagnostics before deciding whether to continue provisioning.

For more complex templates, the host traverses parsed.LogicTree and supplies values in the correct scope:

Field Evaluated XML convention
Content with atom C1 <C1>formatted text</C1>
Condition with atom C2 <C2b>true</C2b> or <C2b>false</C2b>
List with atom L3 <L3><L3i>item-scoped values</L3i><L3i>…</L3i></L3>

Names above are illustrative: use the atoms in the returned metadata. List expressions are evaluated in the enclosing scope, and their child expressions in each item scope. Inline-list punctuation has additional generated metadata; a complete host integration should handle the logic tree rather than simply flatten all fields into a dictionary of values.

Library and examples

The package bundles the OpenDocx-maintained OXPT fork. Core APIs include:

  • Normalizer: normalization, field extraction, and placement diagnostics.
  • Templater: field parsing, logic metadata, and compilation to OXPT templates.
  • Assembler: assembly from compiled templates and evaluated XML.
  • Assembler.AssembleDocumentAsync and Composer: assembly/composition involving additional DOCX sources.
  • Validator: Open XML document validation.

The Lambda example shows separate provisioning and assembly handlers with S3 storage. It illustrates an integration pattern, including the boundary where a host must evaluate data. The odx command-line utility exposes normalization, extraction, compilation, assembly, and validation for local use. Run dotnet run --project odx -- --help for its options.

Install, build, and test

The NuGet package is named OpenDocx.NET:

dotnet add package OpenDocx.NET

Version 1.1.8 targets .NET 10. Dependencies include DocumentFormat.OpenXml 3.5.1 and SkiaSharp 4.153.1. Word automation is not required for provisioning or assembly.

Clone with submodules, install the .NET 10 SDK, and run from the repository root:

git submodule update --init --recursive
dotnet build opendocx.sln -c Release
dotnet test OpenDocx.Tests/OpenDocx.Tests.csproj -c Release
dotnet test Lambdas/test/DocxTemplater.Tests/DocxTemplater.Tests.csproj -c Release

The Lambda tests use in-memory S3 storage and require no AWS account. OXPT has its own tests and build guidance in its submodule. Package publishing instructions are in PUBLISHING.md.

License

OpenDocx.NET is licensed under the Mozilla Public License 2.0. It builds on the document-processing capabilities of OpenXmlPowerTools; see the bundled fork for its license and acknowledgments.

Development of OpenDocx is sponsored by Knackly.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.1.8 106 10/21/2026
1.1.7 93 10/13/2026
1.1.6 145 7/31/2026
1.1.5 124 7/22/2026
1.1.4 147 6/18/2026
1.0.3 153 1/3/2026
1.0.2 341 11/10/2025
1.0.1 254 11/5/2025
1.0.0 199 10/31/2025