WixJsonFileExtension 6.3.2

There is a newer version of this package available.
See the version list below for details.
dotnet add package WixJsonFileExtension --version 6.3.2
                    
NuGet\Install-Package WixJsonFileExtension -Version 6.3.2
                    
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="WixJsonFileExtension" Version="6.3.2" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="WixJsonFileExtension" Version="6.3.2" />
                    
Directory.Packages.props
<PackageReference Include="WixJsonFileExtension" />
                    
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 WixJsonFileExtension --version 6.3.2
                    
#r "nuget: WixJsonFileExtension, 6.3.2"
                    
#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 WixJsonFileExtension@6.3.2
                    
#: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=WixJsonFileExtension&version=6.3.2
                    
Install as a Cake Addin
#tool nuget:?package=WixJsonFileExtension&version=6.3.2
                    
Install as a Cake Tool

WixJsonFileExtension

NuGet License: MIT

An extension to Windows Installer XML (WiX) Toolset to create or modify JSON-formatted files during an installation.

Table of Contents

Overview

Windows Installer XML (WiX) is an open-source set of tools to create Windows software installation setups (*.msi), using XML files to define the content and behavior of the setup.

One important step in most setups is to modify configuration files to reflect either settings specified during the setup by the user, or other settings specific to the individual installation. Windows Installer has built-in actions to modify classic ini-files (*.ini), and WiX provides extensions to modify XML files, particularly Application Configuration files (*.exe.config or *.dll.config) used by Microsoft .NET applications.

Today, a third format has become popular, especially in web applications: JSON (JavaScript Object Notation). JSON is commonly used in networking scenarios (REST services, web applications) and as a local data store format, including configuration files. .NET Core and modern .NET applications rely heavily on JSON-formatted configuration files (e.g., appsettings.json).

WixJsonFileExtension fills this gap by providing methods to read, create, modify, and delete values in JSON files (*.json) during software installation. The XML elements provided by this extension work similarly to WiX's existing XmlFile extension.

Status

✅ Full JSONPath Support with Array Operations

The extension now provides comprehensive JSONPath support powered by jsoncons, including:

  • Multi-select queries - Query multiple elements at once with wildcards ($..price, $.store.book[*])
  • Advanced filters - Filter arrays with complex conditions ($.book[?(@.price > 10)])
  • Array operations - Append, insert, and remove array elements
  • Schema validation - Validate JSON files against JSON schemas to prevent configuration corruption

All operations are performed during installation with full rollback support.

Compatibility

WiX Toolset Version Support

This extension is built for WiX Toolset v4 and later (including v5 and v6). The current package uses WiX Toolset SDK 6.0.1 and targets .NET Standard 2.0 for broad compatibility.

WiX Version Support Status Notes
WiX 6.x ✅ Fully Supported Current target version (WiX 6.0.1)
WiX 5.x ✅ Fully Supported Compatible via .NET Standard 2.0
WiX 4.x ✅ Fully Supported Compatible via .NET Standard 2.0
WiX 3.x ❌ Not Supported Use WiX 4+ for this extension

Package distribution: The extension is distributed via NuGet and includes binaries in both the wixext5/ and wixext6/ package folders to support WiX v5 and v6 installations.

.NET Application Compatibility

This extension works with all .NET application types that use JSON configuration files:

Application Type Configuration Files Support Status
.NET 6+ / .NET Core appsettings.json, appsettings.{Environment}.json ✅ Fully Supported
.NET Framework app.config, web.config (XML), custom JSON files ✅ JSON files supported
ASP.NET Core appsettings.json ✅ Fully Supported
Console Apps appsettings.json, custom JSON configs ✅ Fully Supported
Windows Services appsettings.json, custom JSON configs ✅ Fully Supported

Hybrid scenarios: Applications using both classic .config (XML) and modern JSON files can leverage both WiX's built-in XML handling and this extension for JSON files.

Prerequisites

  • WiX Toolset: Version 4.x or later (for WiX v5/v6 support)
  • .NET: .NET Standard 2.0 compatible environment for building installers
  • Windows: Windows operating system for running the MSI installers

Installation

Add the WixJsonFileExtension package to your WiX installer project:

Using .NET CLI

dotnet add package WixJsonFileExtension

Using Package Manager Console

Install-Package WixJsonFileExtension

Using PackageReference in .csproj/.wixproj

<PackageReference Include="WixJsonFileExtension" Version="6.0.0" />

Quick Start

  1. Add the namespace to your WiX source file (.wxs):
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"
     xmlns:Json="http://schemas.hegsie.com/wix/JsonExtension">
  1. Use the JsonFile element inside a Component to modify your JSON file:
<Component Id="ProductComponent" Guid="{YOUR-GUID-HERE}">
  <File Id="JsonConfig" Name="appsettings.json" Source="appsettings.json" />
  
  
  <Json:JsonFile 
    Id="SetConnectionString" 
    File="[#JsonConfig]" 
    ElementPath="$.ConnectionStrings.Default" 
    Value="Server=myserver;Database=mydb" 
    Action="setValue" />
</Component>
  1. Build your installer as usual with WiX.

Documentation & Resources

Cookbook - Common Patterns

The Cookbook provides practical recipes and patterns for common JSON configuration scenarios:

Example Fragments

The examples/ directory contains ready-to-use WiX fragment files:

See the Examples README for usage instructions.

Improved Authoring Experience

WixJsonFileExtension v6.0 includes significant improvements to the authoring experience, making it easier to write and maintain WiX installers that manipulate JSON files.

XSD Schema with IntelliSense

The extension now includes a comprehensive XSD schema file (json.xsd) that provides rich IntelliSense support in Visual Studio, VS Code, and other XML editors.

Features:

  • 📝 Detailed attribute descriptions - Hover over any attribute to see usage information
  • 🎯 Strict typing for Action attribute - Enum values with descriptions for each action type
  • ✅ Validation at design time - Catch errors before building
  • 🔍 Auto-completion - All elements and attributes available in IntelliSense
  • 📖 Context-sensitive help - Understand what each attribute does while authoring

Using the XSD Schema:

The schema is automatically available when you reference the extension namespace:

<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"
     xmlns:Json="http://schemas.hegsie.com/wix/JsonExtension">

For VS Code users: Configure the XML extension to get full IntelliSense support. The schema file is included in the NuGet package under the schemas/ folder.

Composite Elements for Common Patterns

Instead of writing verbose JsonFile elements for common .NET configuration patterns, you can now use high-level composite elements that expand into the appropriate low-level operations.

Json:AppSettings

Simplified syntax for .NET application settings:


<Json:JsonFile 
  Id="SetEnvironment" 
  File="[#AppSettings]" 
  ElementPath="$.ApplicationSettings.Environment" 
  Value="Production" 
  Action="setValue" />


<Json:AppSettings 
  Id="SetEnvironment" 
  File="[#AppSettings]" 
  Key="ApplicationSettings.Environment" 
  Value="Production" />

Attributes:

  • Key - Dot-notation path (automatically converted to JSONPath)
  • Value - The value to set
  • CreateIfMissing - (Optional) Create nested path if it doesn't exist (default: yes)
Json:ConnectionString

Simplified syntax for database connection strings:


<Json:JsonFile 
  Id="SetConnection" 
  File="[#AppSettings]" 
  ElementPath="$.ConnectionStrings.DefaultConnection" 
  Value="Server=myserver;Database=mydb;" 
  Action="setValue" />


<Json:ConnectionString 
  Id="SetConnection" 
  File="[#AppSettings]" 
  Name="DefaultConnection" 
  Value="Server=myserver;Database=mydb;" />

Attributes:

  • Name - The connection string name (e.g., "DefaultConnection")
  • Value - The connection string value

Security Note: The composite element includes IntelliSense documentation warning about storing passwords in plain text and recommending Integrated Security or external secret management.

Json:LoggingLevel

Simplified syntax for .NET logging configuration:


<Json:JsonFile 
  Id="SetLogLevel" 
  File="[#AppSettings]" 
  ElementPath="$.Logging.LogLevel.Default" 
  Value="Information" 
  Action="setValue" />


<Json:LoggingLevel 
  Id="SetLogLevel" 
  File="[#AppSettings]" 
  Category="Default" 
  Level="Information" />

Attributes:

  • Category - Logging category (default: "Default", also supports "Microsoft", "System", or custom namespaces)
  • Level - Log level (Trace, Debug, Information, Warning, Error, Critical, None)

Benefits of Composite Elements:

  • 🎯 Cleaner syntax - Less verbose than low-level JsonFile elements
  • 📚 Self-documenting - Element names clearly indicate purpose
  • ✅ Type safety - Specific attributes for each pattern
  • 🔒 Best practices - Follows .NET conventions automatically
  • 📖 Better IntelliSense - Contextual help for .NET-specific configurations

Grouping with JsonTransaction

For complex JSON manipulations that involve multiple related edits, you can now use the JsonTransaction element to group operations together for cleaner authoring.

Example:

<Component Id="DatabaseConfig" Guid="{YOUR-GUID}">
  <File Id="AppSettings" Name="appsettings.json" Source="appsettings.json" />
  
  
  <Json:JsonTransaction 
    Id="DatabaseConfiguration" 
    File="[#AppSettings]" 
    BaseSequence="10">
    
    
    
    <Json:JsonFile 
      Id="SetConnection" 
      ElementPath="$.ConnectionStrings.DefaultConnection" 
      Value="Server=[DB_SERVER];Database=[DB_NAME];" 
      Action="setValue" />
    
    <Json:JsonFile 
      Id="SetTimeout" 
      ElementPath="$.Database.CommandTimeout" 
      Value="30" 
      Action="setValue" />
    
    <Json:JsonFile 
      Id="SetRetry" 
      ElementPath="$.Database.MaxRetryCount" 
      Value="3" 
      Action="setValue" />
    
    <Json:JsonFile 
      Id="SetPoolSize" 
      ElementPath="$.Database.MaxPoolSize" 
      Value="100" 
      Action="setValue" />
  </Json:JsonTransaction>
</Component>

Attributes:

  • Id - Unique identifier for the transaction group
  • File - (Optional) Default file path for all nested JsonFile elements
  • BaseSequence - (Optional) Base sequence number for automatic sequencing (default: 1)

Benefits:

  • 🗂️ Logical grouping - Related operations are visually grouped together
  • 🔢 Automatic sequencing - No need to manually assign sequence numbers
  • 📝 Reduced repetition - File path specified once for all operations
  • 📚 Better maintainability - Easier to understand and modify complex configurations
  • 🎯 Clear intent - Transaction name documents the purpose of the grouped operations

Complete Example:

<Component Id="AppConfig" Guid="{YOUR-GUID}">
  <File Id="ProdConfig" Name="appsettings.Production.json" Source="appsettings.json" />
  
  
  <Json:ConnectionString 
    Id="MainDb" 
    File="[#ProdConfig]" 
    Name="MainDatabase" 
    Value="Server=[DB_SERVER];Database=[DB_NAME];Integrated Security=true;" />
  
  <Json:LoggingLevel 
    Id="DefaultLog" 
    File="[#ProdConfig]" 
    Level="Warning" />
  
  
  <Json:JsonTransaction 
    Id="CacheConfiguration" 
    File="[#ProdConfig]" 
    BaseSequence="10">
    
    <Json:JsonFile 
      Id="EnableCache" 
      ElementPath="$.CacheSettings.EnableCaching" 
      Value="true" 
      Action="setValue" />
    
    <Json:JsonFile 
      Id="CacheExpiry" 
      ElementPath="$.CacheSettings.DefaultExpirationMinutes" 
      Value="60" 
      Action="setValue" />
  </Json:JsonTransaction>
</Component>

See CompositeElements.wxs for more comprehensive examples.

Detailed Usage

Available Actions

The JsonFile element supports the following actions:

Action Description ElementPath Type Use Case
readValue Reads a value from the JSON file and stores it in a Windows Installer property JSONPath Reading configuration values to use elsewhere in the installer
setValue Sets or updates a value at the specified path (default action if not specified) JSONPath Updating existing JSON properties
deleteValue Deletes the value(s) at the specified path JSONPath Removing configuration entries
replaceJsonValue Replaces an entire JSON object or array with new JSON content JSONPath Replacing complex nested structures
createJsonPointerValue Creates a new value using JSONPointer syntax (useful for creating nested paths that don't exist) JSONPointer Creating new configuration sections
appendArray Appends a value to an array JSONPath Adding new items to configuration arrays
insertArray Inserts a value at a specific index in an array JSONPath Adding items at specific positions in arrays
removeArrayElement Removes element(s) from an array by value or path JSONPath Removing specific items from configuration arrays
distinctValues Removes duplicate values from an array JSONPath Ensuring unique values in configuration arrays

JsonFile Element Attributes

Attribute Required Description
Id Yes Unique identifier for this JSON file modification
File Yes Path to the JSON file to modify. Can use file references like [#FileId] or formatted paths like [INSTALLFOLDER]config.json
ElementPath Yes JSONPath or JSONPointer expression to locate the element(s) to modify
Action No The action to perform (see Available Actions table). Defaults to setValue
Value Conditional The value to set. Required for setValue, replaceJsonValue, createJsonPointerValue, appendArray, and insertArray actions. For removeArrayElement, Value is optional: if omitted, elements matched by the JSONPath expression are removed; if provided, all array elements matching that value are removed. Can be a simple value, property reference like [PROPERTY_NAME], or JSON-formatted string
DefaultValue No Default value to use if the path doesn't exist (used with readValue)
Property Conditional Windows Installer property to store the read value. Required for readValue action
Sequence No Order in which modifications are applied (default: 1). Lower numbers execute first. Use this to ensure JSON changes happen before services start or in a specific order. All JSON modifications run after InstallFiles and before StartServices in the standard InstallExecuteSequence
Index No For insertArray action: specifies the index at which to insert. Use -1 or omit to append to end
SchemaFile No Path to a JSON schema file for validation. The JSON file will be validated against this schema after modifications
OnlyIfExists No When set to yes, the action is only performed if the ElementPath already exists in the JSON file. This is useful for conditional updates that should only modify existing values without creating new ones. Applies to setValue, createJsonPointerValue, and replaceJsonValue actions. Default is no

JSONPath vs JSONPointer

This extension supports two syntaxes for navigating JSON structures:

JSONPath (Default)

JSONPath is the default syntax and is used for most actions. It's more powerful for querying and supports wildcards and filters.

Syntax examples:

  • $.store.book - Access the 'book' property under 'store'
  • $.store.book[0] - First book in the array
  • $.store.book[?(@.isbn == '0-553-21311-3')] - Filter books by ISBN
  • $..price - All 'price' properties at any depth

Special note: Square brackets in JSONPath must be escaped in WiX XML as [\[] and [\]] because they are MSI formatting characters.

JSONPointer

JSONPointer is used specifically with the createJsonPointerValue action. It's simpler and better suited for creating new paths.

Syntax examples:

  • /store/book - Access the 'book' property under 'store'
  • /store/book/0 - First book in the array
  • /NonExisting/Path - Creates nested path that doesn't exist

Examples

All examples assume you have a JSON file installed as part of your component:

<Component Id="ProductComponent" Guid="{YOUR-GUID-HERE}">
  <File Id="JsonConfig" Name="appsettings.json" Source="appsettings.json" />
  
</Component>

Reading Values

Read a value from JSON and store it in a Windows Installer property:


<Json:JsonFile 
  Id="ReadBookCategory" 
  File="[#JsonConfig]" 
  ElementPath="$.store.book[\[]?(@.isbn == '0-553-21311-3')[\]].category" 
  DefaultValue="Unknown" 
  Action="readValue" 
  Property="BOOK_CATEGORY" />


<Json:JsonFile 
  Id="ReadMissingValue" 
  File="[#JsonConfig]" 
  ElementPath="$.nonexistent.path" 
  DefaultValue="DefaultValue" 
  Action="readValue" 
  Property="MY_PROPERTY" />

Setting Values

Update or set a simple value:


<Json:JsonFile 
  Id="SetLogLevel" 
  File="[#JsonConfig]" 
  ElementPath="$.Logging.LogLevel.Default" 
  Value="Information" 
  Action="setValue" />


<Json:JsonFile 
  Id="SetPrice" 
  File="[#JsonConfig]" 
  ElementPath="$.store.book[\[]?(@.isbn == '0-553-21311-3')[\]].price" 
  Value="12.99" 
  Action="setValue" />


<Json:JsonFile 
  Id="SetFromProperty" 
  File="[#JsonConfig]" 
  ElementPath="$.store.book[\[]?(@.isbn == '0-553-21311-3')[\]].category" 
  Value="[USER_SELECTED_CATEGORY]" 
  Action="setValue" />

Replacing JSON Objects

Replace an entire JSON object or array with new content:


<Json:JsonFile 
  Id="ReplaceBooks" 
  File="[#JsonConfig]" 
  ElementPath="$.store.book" 
  Value="[MY_BOOKS_JSON]" 
  Action="replaceJsonValue" />

The MY_BOOKS_JSON property would contain valid JSON like:

[{"title": "New Book", "author": "John Doe", "price": 15.99}]

Deleting Values

Remove values from the JSON file:


<Json:JsonFile 
  Id="DeleteOldPrice" 
  File="[#JsonConfig]" 
  ElementPath="$.store.bicycle.OldPrice" 
  Action="deleteValue" />


<Json:JsonFile 
  Id="DeleteAllOldPrices" 
  File="[#JsonConfig]" 
  ElementPath="$..book[\[]*[\]].OldPrice" 
  Action="deleteValue" />

Creating New Values with JSONPointer

Create new configuration paths that don't exist:


<Json:JsonFile 
  Id="CreateNewSetting" 
  File="[#JsonConfig]" 
  ElementPath="/NewSection/MySetting" 
  Value="MyValue" 
  Action="createJsonPointerValue" />

This will create the structure:

{
  "NewSection": {
    "MySetting": "MyValue"
  }
}

Complex Nested Paths

Working with deeply nested JSON structures:


<Json:JsonFile 
  Id="UpdateLogPath" 
  File="[#JsonConfig]" 
  ElementPath="$.Serilog.WriteTo[\[]0[\]].Args.configure[\[]0[\]].Args.path" 
  Value="[LOG_DIRECTORY]MyApp\\Logs\\app.log" />

For the JSON structure:

{
  "Serilog": {
    "WriteTo": [
      {
        "Args": {
          "configure": [
            {
              "Args": {
                "path": "C:\\Logs\\app.log"
              }
            }
          ]
        }
      }
    ]
  }
}

Advanced Features

Automatic Rollback Support

WixJsonFileExtension automatically provides rollback support for all JSON file modifications. If an installation fails or is rolled back, all JSON files are restored to their original state.

How it works:

  • Before modifying any JSON file, the extension saves a backup of the original file content
  • If the installation fails or is cancelled, the rollback custom action automatically restores all modified files
  • The file's last modified timestamp is also preserved during rollback
  • Rollback happens automatically - no additional configuration is required

Example:

<Json:JsonFile 
  Id="SetConnectionString" 
  File="[#JsonConfig]" 
  ElementPath="$.ConnectionStrings.Default" 
  Value="Server=myserver;Database=mydb" 
  Action="setValue" />

If the installation fails after this modification, the appsettings.json file will be automatically restored to its original state.

Scheduling and Service Dependencies

To ensure JSON configuration changes are applied before Windows services start or applications launch, use the Sequence attribute to control the order of operations.

Best Practices:

  1. Use lower sequence numbers (e.g., 1-10) for critical configuration that must be set first
  2. Use higher sequence numbers (e.g., 100+) for optional configuration
  3. JSON modifications run after InstallFiles by default, which is before StartServices

Example - Configure before service starts:

<Component Id="AppComponent" Guid="{YOUR-GUID}">
  <File Id="AppConfig" Name="appsettings.json" Source="appsettings.json" />
  
  
  <Json:JsonFile 
    Id="SetDatabaseConnection" 
    File="[#AppConfig]" 
    ElementPath="$.Database.ConnectionString" 
    Value="[DATABASE_CONNECTION]" 
    Sequence="1" />
    
  <Json:JsonFile 
    Id="SetLogging" 
    File="[#AppConfig]" 
    ElementPath="$.Logging.Level" 
    Value="Information" 
    Sequence="2" />
  
  
  <ServiceInstall 
    Id="MyAppService"
    Name="MyApp"
    Type="ownProcess"
    Start="auto"
    ErrorControl="normal"
    Description="My Application Service" />
    
  <ServiceControl 
    Id="StartService"
    Name="MyApp"
    Start="install"
    Stop="both"
    Remove="uninstall" />
</Component>

Execution Order:

  1. Files are installed (including appsettings.json)
  2. JSON modifications run (in sequence order)
  3. Services are started

This ensures your application configuration is ready before the service attempts to read it.

Creating New JSON Files

While the extension primarily modifies existing JSON files, you can create new configuration files using the createJsonPointerValue action. For more complex scenarios, combine file installation with JSON modifications.

Scenario 1: Create new settings in an existing file

<Json:JsonFile 
  Id="CreateNewSettings" 
  File="[#JsonConfig]" 
  ElementPath="/AppSettings/NewFeature/Enabled" 
  Value="true" 
  Action="createJsonPointerValue" />

This creates nested structure:

{
  "AppSettings": {
    "NewFeature": {
      "Enabled": true
    }
  }
}

Scenario 2: Start from a template file

Ship a template JSON file with your installer and modify it during installation:

<Component Id="ConfigComponent" Guid="{YOUR-GUID}">
  
  <File Id="ConfigTemplate" Name="config.json" Source="config.template.json" />
  
  
  <Json:JsonFile 
    Id="SetInstallPath" 
    File="[#ConfigTemplate]" 
    ElementPath="$.Installation.Path" 
    Value="[INSTALLFOLDER]" 
    Sequence="1" />
    
  <Json:JsonFile 
    Id="SetMachineName" 
    File="[#ConfigTemplate]" 
    ElementPath="$.Installation.MachineName" 
    Value="[COMPUTERNAME]" 
    Sequence="2" />
</Component>

Scenario 3: Create minimal JSON from scratch

Use multiple createJsonPointerValue operations to build a configuration file:

<Component Id="MinimalConfig" Guid="{YOUR-GUID}">
  
  <File Id="EmptyConfig" Name="settings.json" Source="empty.json" />
  
  
  <Json:JsonFile 
    Id="CreateAppName" 
    File="[#EmptyConfig]" 
    ElementPath="/Application/Name" 
    Value="MyApplication" 
    Action="createJsonPointerValue" 
    Sequence="1" />
    
  <Json:JsonFile 
    Id="CreateAppVersion" 
    File="[#EmptyConfig]" 
    ElementPath="/Application/Version" 
    Value="1.0.0" 
    Action="createJsonPointerValue" 
    Sequence="2" />
    
  <Json:JsonFile 
    Id="CreateDbConnection" 
    File="[#EmptyConfig]" 
    ElementPath="/Database/ConnectionString" 
    Value="[CONNECTION_STRING]" 
    Action="createJsonPointerValue" 
    Sequence="3" />
</Component>

Where empty.json contains:

{}

Working with Arrays

Appending to Arrays

Add new elements to the end of an array:


<Json:JsonFile 
  Id="AppendBook" 
  File="[#JsonConfig]" 
  ElementPath="$.store.book" 
  Value='{"category":"science","author":"Carl Sagan","title":"Cosmos","price":14.99}' 
  Action="appendArray" />
Inserting into Arrays

Insert elements at specific positions:


<Json:JsonFile 
  Id="InsertBook" 
  File="[#JsonConfig]" 
  ElementPath="$.store.book" 
  Value='{"category":"biography","author":"Walter Isaacson","title":"Steve Jobs","price":18.99}' 
  Action="insertArray"
  Index="0" />


<Json:JsonFile 
  Id="AppendBookWithIndex" 
  File="[#JsonConfig]" 
  ElementPath="$.store.book" 
  Value='{"category":"history","author":"Yuval Noah Harari","title":"Sapiens","price":16.99}' 
  Action="insertArray"
  Index="-1" />
Removing Array Elements

Remove elements by matching value:


<Json:JsonFile 
  Id="RemoveBook" 
  File="[#JsonConfig]" 
  ElementPath="$.store.book" 
  Value='{"isbn":"0-553-21311-3"}' 
  Action="removeArrayElement" />


<Json:JsonFile 
  Id="RemoveExpensiveBooks" 
  File="[#JsonConfig]" 
  ElementPath="$.store.book[\[]?(@.price > 20)[\]]" 
  Action="removeArrayElement" />
Removing Duplicates from Arrays

Remove duplicate values from an array to ensure uniqueness:


<Json:JsonFile 
  Id="DeduplicateTags" 
  File="[#JsonConfig]" 
  ElementPath="$.configuration.tags" 
  Action="distinctValues" />


<Json:JsonFile 
  Id="DeduplicateFeatureFlags" 
  File="[#JsonConfig]" 
  ElementPath="$.features.enabled" 
  Action="distinctValues" />

The distinctValues action compares array elements by their serialized JSON representation, so it works with both simple values (strings, numbers) and complex objects. For example:

Before:

{
  "tags": ["production", "webapp", "production", "backend", "webapp"]
}

After distinctValues:

{
  "tags": ["production", "webapp", "backend"]
}

Conditional Updates with OnlyIfExists

The OnlyIfExists attribute allows you to conditionally update JSON values only if they already exist. This is useful when you want to modify existing configuration without creating new entries.


<Json:JsonFile 
  Id="UpdateConnectionString" 
  File="[#JsonConfig]" 
  ElementPath="$.ConnectionStrings.Database" 
  Value="Server=myserver;Database=mydb;" 
  Action="setValue"
  OnlyIfExists="yes" />


<Json:JsonFile 
  Id="UpdateLogLevel" 
  File="[#JsonConfig]" 
  ElementPath="$.Logging.LogLevel.Default" 
  Value="Warning" 
  Action="setValue"
  OnlyIfExists="yes" />


<Json:JsonFile 
  Id="ConditionalFeature" 
  File="[#JsonConfig]" 
  ElementPath="/Features/NewFeature/Enabled" 
  Value="true" 
  Action="createJsonPointerValue"
  OnlyIfExists="yes" />

Use Cases for OnlyIfExists:

  • Updating optional configuration sections that may not be present in all deployments
  • Modifying settings only in environments where they're already configured
  • Avoiding creation of unnecessary configuration entries
  • Safe upgrades that only modify existing settings

Behavior:

  • If OnlyIfExists="yes" and the path exists: the operation proceeds normally
  • If OnlyIfExists="yes" and the path does NOT exist: the operation is skipped (returns success, no error)
  • If OnlyIfExists="no" (or omitted): the operation always proceeds (default behavior)

JSON Schema Validation

Validate JSON files against a schema to ensure data integrity:


<Json:JsonFile 
  Id="SetServerWithValidation" 
  File="[#JsonConfig]" 
  ElementPath="$.ConnectionStrings.Database" 
  Value="Server=myserver;Database=mydb;Trusted_Connection=True;" 
  Action="setValue"
  SchemaFile="[INSTALLFOLDER]config-schema.json" />

Example schema file (config-schema.json):

{
  "type": "object",
  "required": ["ConnectionStrings", "Logging"],
  "properties": {
    "ConnectionStrings": {
      "type": "object",
      "properties": {
        "Database": {
          "type": "string"
        }
      }
    },
    "Logging": {
      "type": "object",
      "properties": {
        "LogLevel": {
          "type": "object"
        }
      }
    }
  }
}

The installer will fail if the modified JSON does not conform to the schema, preventing configuration corruption.

Schema Validation Capabilities:

The extension provides basic JSON Schema validation including:

  • ✅ Root type validation (object, array, string, number, boolean, null)
  • ✅ Required properties checking
  • ✅ Property type validation
  • ✅ Basic integer/number type matching

Limitations:

  • ❌ Integer values are not checked for whole numbers (any numeric value is accepted)
  • ❌ $ref references not supported
  • ❌ Pattern, enum, min/max constraints not validated
  • ❌ Complex schemas with conditional logic (if/then/else) not supported
  • ❌ Format validation not implemented

For more complex validation needs, consider pre-validating your JSON files separately or using a dedicated JSON Schema validation tool.

Advanced JSONPath Features

The extension supports complex JSONPath expressions powered by jsoncons:

Multi-Select Queries

Select multiple elements at once:


<Json:JsonFile 
  Id="UpdateAllPrices" 
  File="[#JsonConfig]" 
  ElementPath="$..price" 
  Value="9.99" 
  Action="setValue" />

Behavior with Multiple Matches:

  • setValue: All matched elements are updated with the same value
  • deleteValue: All matched elements are deleted
  • replaceJsonValue: All matched elements are replaced with the new JSON value
  • readValue: Returns the first matched element's value
  • Array operations: Apply to all matched arrays

This allows for powerful bulk operations. For example, $..price will update every price property at any depth in the JSON structure.

Array Filters

Use filters to select specific array elements:


<Json:JsonFile 
  Id="UpdateMelvilleBooks" 
  File="[#JsonConfig]" 
  ElementPath="$.store.book[\[]?(@.author == 'Herman Melville')[\]].price" 
  Value="12.99" 
  Action="setValue" />


<Json:JsonFile 
  Id="UpdateFictionBooks" 
  File="[#JsonConfig]" 
  ElementPath="$.store.book[\[]?(@.category == 'fiction')[\]].price" 
  Value="11.99" 
  Action="setValue" />

Filter Examples:

  • $.store.book[?(@.price < 10)] - Select all books cheaper than 10
  • $.store.book[?(@.isbn)] - Select all books that have an ISBN property
  • $.store.book[?(@.category == 'fiction')] - Select all fiction books

When a filter matches multiple elements, the action is applied to all matching elements.

Wildcards

Use wildcards to match any element:


<Json:JsonFile 
  Id="ReadAllBooks" 
  File="[#JsonConfig]" 
  ElementPath="$.store.book[\[]*[\]]" 
  Action="readValue"
  Property="ALL_BOOKS" />
Complex Filters

Combine multiple conditions:


<Json:JsonFile 
  Id="UpdateExpensiveFiction" 
  File="[#JsonConfig]" 
  ElementPath="$.store.book[\[]?(@.category == 'fiction' &amp;&amp; @.price > 10)[\]].price" 
  Value="15.99" 
  Action="setValue" />

Note: When using logical operators in XML, remember to escape them:

  • && becomes &amp;&amp;
  • || becomes || (no escaping needed)
  • < becomes &lt;
  • > becomes &gt;

Common .NET Configuration Patterns

This section provides convenience patterns for typical .NET application configuration scenarios, reducing boilerplate and following .NET conventions.

Connection Strings

Modern .NET applications commonly store connection strings in the ConnectionStrings section of appsettings.json:

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=localhost;Database=MyDb;",
    "ReportingConnection": "Server=reporting;Database=Reports;"
  }
}

Setting a default connection string during installation:

<Component Id="DatabaseConfig" Guid="{YOUR-GUID}">
  <File Id="AppSettings" Name="appsettings.json" Source="appsettings.json" />
  
  
  <Json:JsonFile 
    Id="SetDefaultConnection" 
    File="[#AppSettings]" 
    ElementPath="$.ConnectionStrings.DefaultConnection" 
    Value="Server=[DB_SERVER];Database=[DB_NAME];User Id=[DB_USER];Password=[DB_PASSWORD];" 
    Action="setValue" />
    
</Component>

Creating connection strings section if it doesn't exist:


<Json:JsonFile 
  Id="CreateConnectionStrings" 
  File="[#AppSettings]" 
  ElementPath="/ConnectionStrings/DefaultConnection" 
  Value="Server=[DB_SERVER];Database=[DB_NAME];Integrated Security=true;" 
  Action="createJsonPointerValue" />

Logging Configuration

.NET applications use structured logging configuration in appsettings.json:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "System": "Warning"
    }
  }
}

Setting log levels during installation:

<Component Id="LoggingConfig" Guid="{YOUR-GUID}">
  <File Id="AppSettings" Name="appsettings.json" Source="appsettings.json" />
  
  
  <Json:JsonFile 
    Id="SetDefaultLogLevel" 
    File="[#AppSettings]" 
    ElementPath="$.Logging.LogLevel.Default" 
    Value="[LOG_LEVEL]" 
    Action="setValue" />
  
  
  <Json:JsonFile 
    Id="SetMicrosoftLogLevel" 
    File="[#AppSettings]" 
    ElementPath="$.Logging.LogLevel.Microsoft" 
    Value="Warning" 
    Action="setValue" />
</Component>

Common log level values: Trace, Debug, Information, Warning, Error, Critical, None

Serilog Configuration

For applications using Serilog, you can configure file paths and settings:


<Json:JsonFile 
  Id="SetSerilogPath" 
  File="[#AppSettings]" 
  ElementPath="$.Serilog.WriteTo[\[]0[\]].Args.path" 
  Value="[INSTALLFOLDER]Logs\\application.log" 
  Action="setValue" />


<Json:JsonFile 
  Id="SetSerilogMinLevel" 
  File="[#AppSettings]" 
  ElementPath="$.Serilog.MinimumLevel.Default" 
  Value="Information" 
  Action="setValue" />

ASP.NET Core Application Settings

Setting application URLs:


<Json:JsonFile 
  Id="SetApplicationUrl" 
  File="[#AppSettings]" 
  ElementPath="$.Urls" 
  Value="http://localhost:[APP_PORT]" 
  Action="setValue" />

Configuring CORS origins:


<Json:JsonFile 
  Id="SetCorsOrigins" 
  File="[#AppSettings]" 
  ElementPath="$.Cors.AllowedOrigins" 
  Value='["http://localhost:3000","https://[DOMAIN_NAME]"]' 
  Action="replaceJsonValue" />

Environment-Specific Configuration

.NET applications often use environment-specific configuration files like appsettings.Production.json:

<Component Id="ProductionConfig" Guid="{YOUR-GUID}">
  <File Id="AppSettingsProd" Name="appsettings.Production.json" Source="appsettings.Production.json" />
  
  
  <Json:JsonFile 
    Id="SetProdConnection" 
    File="[#AppSettingsProd]" 
    ElementPath="$.ConnectionStrings.DefaultConnection" 
    Value="Server=[PROD_DB_SERVER];Database=[PROD_DB_NAME];Integrated Security=true;" 
    Action="setValue" />
  
  
  <Json:JsonFile 
    Id="SetProdLogLevel" 
    File="[#AppSettingsProd]" 
    ElementPath="$.Logging.LogLevel.Default" 
    Value="Warning" 
    Action="setValue" />
</Component>

Service Configuration

Windows Service settings:


<Json:JsonFile 
  Id="SetServiceName" 
  File="[#AppSettings]" 
  ElementPath="$.ServiceSettings.ServiceName" 
  Value="[SERVICE_NAME]" 
  Action="setValue" />

<Json:JsonFile 
  Id="SetServiceDisplay" 
  File="[#AppSettings]" 
  ElementPath="$.ServiceSettings.DisplayName" 
  Value="[SERVICE_DISPLAY_NAME]" 
  Action="setValue" />

API Keys and Secrets

Note: For production scenarios, consider using secure key storage (Azure Key Vault, AWS Secrets Manager) instead of storing secrets in configuration files.


<Json:JsonFile 
  Id="SetApiKey" 
  File="[#AppSettings]" 
  ElementPath="$.ApiSettings.ApiKey" 
  Value="[API_KEY]" 
  Action="setValue" />

Complete Example: Typical .NET 6+ Application

Here's a complete example combining common patterns:

<Component Id="DotNetAppConfig" Guid="{YOUR-GUID}">
  <File Id="AppSettings" Name="appsettings.json" Source="appsettings.json" />
  
  
  <Json:JsonFile 
    Id="SetDefaultConnection" 
    File="[#AppSettings]" 
    ElementPath="$.ConnectionStrings.DefaultConnection" 
    Value="Server=[DB_SERVER];Database=[DB_NAME];Integrated Security=true;" 
    Action="setValue" />
  
  
  <Json:JsonFile 
    Id="SetLogLevel" 
    File="[#AppSettings]" 
    ElementPath="$.Logging.LogLevel.Default" 
    Value="[LOG_LEVEL]" 
    Action="setValue" />
  
  
  <Json:JsonFile 
    Id="SetAppUrl" 
    File="[#AppSettings]" 
    ElementPath="$.Urls" 
    Value="http://localhost:[APP_PORT]" 
    Action="setValue" />
  
  
  <Json:JsonFile 
    Id="SetDataPath" 
    File="[#AppSettings]" 
    ElementPath="$.ApplicationSettings.DataPath" 
    Value="[INSTALLFOLDER]Data" 
    Action="setValue" />
</Component>

Best Practices for .NET Configuration

  1. Use installer properties: Define properties in your WiX project for configurable values, allowing users to customize the installation
  2. Provide defaults: Always set sensible default values in your source appsettings.json
  3. Environment-specific files: Use separate configuration files for different environments
  4. Sequence operations: Use the Sequence attribute when operations depend on each other
  5. Test paths: Verify JSONPath expressions work with your JSON structure before deploying
  6. Security: Never store secrets in plain text; use secure configuration providers in production
  7. Validation: Use readValue action to verify existing configuration before modifying

Escaping Special Characters

When working with JSONPath in WiX XML files, you need to be aware of multiple layers of escaping:

1. MSI Formatting Characters

Square brackets [ and ] are special characters in Windows Installer formatted strings. They must be escaped as [\[] and [\]] in your WiX source files.

Example:


ElementPath="$.store.book[0].title"


ElementPath="$.store.book[\[]0[\]].title"

2. JSONPath Escaping

If your JSON keys contain special characters or if you need literal backslashes in paths, remember that JSONPath uses backslashes for escaping.

Example for a JSON key with a dot:

ElementPath="$.store['book.special'].title"

3. File Paths in Values

When setting file paths as values, remember that Windows uses backslashes, and you may need to escape them:


Value="C:\\Program Files\\MyApp\\config.json"


Value="[INSTALLFOLDER]config.json"

Troubleshooting

Common Issues

  1. "Element not found" errors

    • Verify your JSONPath is correct using online JSONPath evaluators like JSONPath Online Evaluator
    • Check that square brackets are properly escaped: [\[] and [\]]
    • Ensure the JSON file is valid and well-formed
    • Look for "WixJsonFile: Error" messages in the MSI log for specific path details
  2. Value not being updated

    • Check the Sequence attribute if you have multiple modifications
    • Ensure the path exists before trying to set a value (or use createJsonPointerValue)
    • Verify the file reference [#FileId] matches your File element's Id
  3. Array operations failing

    • Ensure the ElementPath points to an array when using appendArray, insertArray, or removeArrayElement
    • For insertArray, verify the Index is valid or use -1 to append
    • For removeArrayElement, ensure the Value matches the element structure you want to remove
  4. Schema validation errors

    • Verify the schema file path is correct and the file is accessible during installation
    • Check that the schema file is valid JSON Schema format
    • Review the MSI log for specific validation error messages
    • Note: This extension provides basic schema validation (type checking, required properties). For full JSON Schema Draft 7+ support, ensure your schemas use supported features
  5. Property values not expanding

    • Ensure property names are in uppercase and enclosed in brackets: [MY_PROPERTY]
    • Check that the property is set before the JsonFile action executes
    • The compiler will now warn you about lowercase property references
  6. Build-time warnings

    • The extension now provides compile-time diagnostics for common issues:
      • Unescaped brackets: Warns if square brackets aren't properly escaped as [\[] and [\]]
      • Invalid JSONPath syntax: Detects basic syntax errors in ElementPath
      • Missing required attributes: Ensures Value is present for setValue/replaceJsonValue actions
      • Property name format: Warns if property names aren't uppercase

Debugging Tips

  • Use the readValue action to verify paths are correct before modifying
  • Test your JSONPath expressions in an online evaluator like jsonpath.com
  • For complex queries, test with the jsoncons library directly or use its online playground
  • Check the MSI log file for detailed error messages: msiexec /i YourInstaller.msi /l*v install.log
  • Search the log for WixJsonFile: to find all JSON-related operations and errors
  • All error messages now include the affected file path and element path for easier debugging
  • Refer to the example in TestJsonConfigInstaller/Product.wxs for working patterns
  • Check the Cookbook for common patterns and best practices
  • When using schema validation, test your JSON against the schema separately first using tools like jsonschemavalidator.net

Understanding Error Messages

The extension now provides structured error messages with the format:

WixJsonFile: Error - [Description] for path '[ElementPath]' in file '[FilePath]'

Common error messages:

  • File not found - The specified JSON file doesn't exist at the given path

  • No elements found at path - The JSONPath query didn't match any elements

  • Invalid element path parameter - The ElementPath attribute is missing or empty

  • Failed to parse JSON value - The Value contains invalid JSON (for replaceJsonValue action)

  • When using schema validation, test your JSON against the schema separately first using tools like jsonschemavalidator.net

Building from Source

Prerequisites

  • Visual Studio 2022 or later
  • WiX Toolset v4 or later
  • .NET SDK 6.0 or later
  • C++ build tools (for native custom action)

Build Steps

  1. Clone the repository:

    git clone https://github.com/hegsie/WixJsonFileExtension.git
    cd WixJsonFileExtension
    
  2. Restore NuGet packages:

    nuget restore WixJsonFileExtension.sln
    
  3. Build the solution:

    msbuild /m /p:Configuration=Release WixJsonFileExtension.sln
    
  4. The built NuGet package will be in:

    src\wixext\bin\Release\WixJsonFileExtension.{version}.nupkg
    

Project Structure

  • src/ca/ - Native C++ custom action (uses jsoncons for JSON manipulation)
  • src/wixext/ - WiX extension (C# compiler and binder extensions)
  • src/wixlib/ - WiX library with custom action definitions
  • TestJsonConfigInstaller/ - Example WiX installer project demonstrating usage

Contributing

Contributions are welcome! Please feel free to submit issues, feature requests, or pull requests.

Reporting Issues

When reporting issues, please include:

  • Your WiX Toolset version
  • A minimal example demonstrating the problem
  • The JSON structure you're working with
  • Any relevant error messages from the MSI log

Testing Your Changes

Before submitting a pull request:

  1. Build the solution successfully
  2. Test with the example installer in TestJsonConfigInstaller/
  3. Ensure your changes don't break existing functionality

Acknowledgements

WixJsonFileExtension uses jsoncons by Daniel Parker to read and manipulate JSON files. Special thanks to Daniel Parker for this excellent C++ JSON library.

License

This project is licensed under the MIT License - see the LICENSE file for details.


Repository: https://github.com/hegsie/WixJsonFileExtension
NuGet Package: https://www.nuget.org/packages/WixJsonFileExtension/
Author: Ben Hegarty (hegsie)

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

This package has no dependencies.

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
7.1.1 487 10/2/2026
7.1.0 253 9/13/2026
7.0.0 1,498 8/11/2026
6.5.0 836 7/6/2026
6.4.0 374 6/3/2026
6.3.3 216 5/23/2026
6.3.2 4,205 1/9/2026
6.3.1 189 12/12/2025
6.3.0 525 11/23/2025
Loading failed