WixJsonFileExtension 6.3.2
See the version list below for details.
dotnet add package WixJsonFileExtension --version 6.3.2
NuGet\Install-Package WixJsonFileExtension -Version 6.3.2
<PackageReference Include="WixJsonFileExtension" Version="6.3.2" />
<PackageVersion Include="WixJsonFileExtension" Version="6.3.2" />
<PackageReference Include="WixJsonFileExtension" />
paket add WixJsonFileExtension --version 6.3.2
#r "nuget: WixJsonFileExtension, 6.3.2"
#:package WixJsonFileExtension@6.3.2
#addin nuget:?package=WixJsonFileExtension&version=6.3.2
#tool nuget:?package=WixJsonFileExtension&version=6.3.2
WixJsonFileExtension
An extension to Windows Installer XML (WiX) Toolset to create or modify JSON-formatted files during an installation.
Table of Contents
- Overview
- Status
- Compatibility
- Prerequisites
- Installation
- Quick Start
- Documentation & Resources
- Improved Authoring Experience
- Detailed Usage
- Examples
- Advanced Features
- Common .NET Configuration Patterns
- Escaping Special Characters
- Troubleshooting
- Building from Source
- Contributing
- Acknowledgements
- License
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
- 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">
- 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>
- Build your installer as usual with WiX.
Documentation & Resources
Cookbook - Common Patterns
The Cookbook provides practical recipes and patterns for common JSON configuration scenarios:
- 📚 Connection Strings - Configure database connections during installation
- 📝 Logging Configuration - Set log levels, file paths, and Serilog settings
- 🚩 Feature Flags - Toggle features based on edition or environment
- 🌐 API Endpoints - Configure API URLs, timeouts, and authentication
- 🔧 Environment-Specific Settings - Deploy configuration for Development, Staging, or Production
- 📐 Complex Nested Configurations - Work with deeply nested JSON structures
- 🔗 Best Practices - Tips for effective JSON file manipulation
Example Fragments
The examples/ directory contains ready-to-use WiX fragment files:
- ConnectionStrings.wxs - Database connection configuration
- LoggingConfiguration.wxs - Logging levels and file paths
- FeatureFlags.wxs - Feature flag management
- ApiEndpoints.wxs - API endpoint configuration
- EnvironmentConfiguration.wxs - Environment-based settings
- AdvancedArrayOperations.wxs ⭐ NEW - Advanced array operations and conditional updates
- CompositeElements.wxs ⭐ NEW - High-level composite elements and JsonTransaction grouping
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 setCreateIfMissing- (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 groupFile- (Optional) Default file path for all nested JsonFile elementsBaseSequence- (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:
- Use lower sequence numbers (e.g., 1-10) for critical configuration that must be set first
- Use higher sequence numbers (e.g., 100+) for optional configuration
- JSON modifications run after
InstallFilesby default, which is beforeStartServices
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:
- Files are installed (including
appsettings.json) - JSON modifications run (in sequence order)
- 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' && @.price > 10)[\]].price"
Value="15.99"
Action="setValue" />
Note: When using logical operators in XML, remember to escape them:
&&becomes&&||becomes||(no escaping needed)<becomes<>becomes>
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
- Use installer properties: Define properties in your WiX project for configurable values, allowing users to customize the installation
- Provide defaults: Always set sensible default values in your source
appsettings.json - Environment-specific files: Use separate configuration files for different environments
- Sequence operations: Use the
Sequenceattribute when operations depend on each other - Test paths: Verify JSONPath expressions work with your JSON structure before deploying
- Security: Never store secrets in plain text; use secure configuration providers in production
- Validation: Use
readValueaction 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
"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
Value not being updated
- Check the
Sequenceattribute 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
- Check the
Array operations failing
- Ensure the ElementPath points to an array when using
appendArray,insertArray, orremoveArrayElement - 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
- Ensure the ElementPath points to an array when using
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
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
- Ensure property names are in uppercase and enclosed in brackets:
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
Valueis present for setValue/replaceJsonValue actions - Property name format: Warns if property names aren't uppercase
- Unescaped brackets: Warns if square brackets aren't properly escaped as
- The extension now provides compile-time diagnostics for common issues:
Debugging Tips
- Use the
readValueaction 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.wxsfor 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 pathNo elements found at path- The JSONPath query didn't match any elementsInvalid element path parameter- The ElementPath attribute is missing or emptyFailed 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
Clone the repository:
git clone https://github.com/hegsie/WixJsonFileExtension.git cd WixJsonFileExtensionRestore NuGet packages:
nuget restore WixJsonFileExtension.slnBuild the solution:
msbuild /m /p:Configuration=Release WixJsonFileExtension.slnThe 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 definitionsTestJsonConfigInstaller/- 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:
- Build the solution successfully
- Test with the example installer in
TestJsonConfigInstaller/ - 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)
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.