WixJsonFileExtension Cookbook
This cookbook provides practical examples and patterns for common JSON configuration scenarios using WixJsonFileExtension with WiX 5+.
Table of Contents
- Connection Strings
- Logging Configuration
- Feature Flags
- API Endpoints
- Environment-Specific Settings
- Complex Nested Configurations
- Array Manipulation
- Conditional Updates
- Uninstall Clean-up
Connection Strings
Pattern: Update Database Connection String
Use Case: Update the database connection string during installation based on user input.
Example appsettings.json:
{
"ConnectionStrings": {
"DefaultConnection": "Server=localhost;Database=MyApp;Trusted_Connection=True;"
}
}
WiX Fragment:
<Component Id="ConfigComponent" Guid="*">
<File Id="AppSettingsJson" Name="appsettings.json" Source="appsettings.json" />
<!-- Update connection string from user property -->
<Json:JsonFile
Id="UpdateConnectionString"
File="[#AppSettingsJson]"
ElementPath="$.ConnectionStrings.DefaultConnection"
Value="[DB_CONNECTION_STRING]"
Action="setValue" />
</Component>
Installer Properties:
<!-- Define property with default value -->
<Property Id="DB_CONNECTION_STRING" Value="Server=localhost;Database=MyApp;Trusted_Connection=True;" />
<!-- Collect from user via UI -->
<UI>
<Dialog Id="DatabaseConfigDlg" Width="370" Height="270" Title="Database Configuration">
<Control Id="ConnectionStringEdit" Type="Edit" X="20" Y="60" Width="330" Height="18"
Property="DB_CONNECTION_STRING" />
</Dialog>
</UI>
Pattern: Multiple Connection Strings
Example appsettings.json:
{
"ConnectionStrings": {
"Primary": "Server=primary;Database=MyApp;",
"Reporting": "Server=reporting;Database=MyApp;",
"Logging": "Server=logging;Database=Logs;"
}
}
WiX Fragment:
<Component Id="ConfigComponent" Guid="*">
<File Id="AppSettingsJson" Name="appsettings.json" Source="appsettings.json" />
<Json:JsonFile Id="UpdatePrimaryDB" File="[#AppSettingsJson]"
ElementPath="$.ConnectionStrings.Primary"
Value="[PRIMARY_DB_CONNECTION]"
Action="setValue" Sequence="1" />
<Json:JsonFile Id="UpdateReportingDB" File="[#AppSettingsJson]"
ElementPath="$.ConnectionStrings.Reporting"
Value="[REPORTING_DB_CONNECTION]"
Action="setValue" Sequence="2" />
<Json:JsonFile Id="UpdateLoggingDB" File="[#AppSettingsJson]"
ElementPath="$.ConnectionStrings.Logging"
Value="[LOGGING_DB_CONNECTION]"
Action="setValue" Sequence="3" />
</Component>
Logging Configuration
Pattern: Configure Log Level
Use Case: Set the logging level based on installation mode (Debug/Production).
Example appsettings.json:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
WiX Fragment:
<Component Id="ConfigComponent" Guid="*">
<File Id="AppSettingsJson" Name="appsettings.json" Source="appsettings.json" />
<!-- Set default log level -->
<Json:JsonFile
Id="SetDefaultLogLevel"
File="[#AppSettingsJson]"
ElementPath="$.Logging.LogLevel.Default"
Value="[LOG_LEVEL]"
Action="setValue" />
<!-- Set Microsoft log level -->
<Json:JsonFile
Id="SetMicrosoftLogLevel"
File="[#AppSettingsJson]"
ElementPath="$.Logging.LogLevel.Microsoft"
Value="Warning"
Action="setValue" />
</Component>
<!-- Property defaults based on build configuration -->
<Property Id="LOG_LEVEL" Value="Information" />
<!-- Set to Debug for development installs -->
<SetProperty Id="LOG_LEVEL" Value="Debug" After="AppSearch" Sequence="first">
<![CDATA[DEVELOPMENT_MODE = "1"]]>
</SetProperty>
Pattern: Configure File Logging Path
Use Case: Set custom log file path during installation.
Example appsettings.json:
{
"Serilog": {
"WriteTo": [
{
"Name": "File",
"Args": {
"path": "C:\\Logs\\app.log",
"rollingInterval": "Day"
}
}
]
}
}
WiX Fragment:
<Component Id="ConfigComponent" Guid="*">
<File Id="AppSettingsJson" Name="appsettings.json" Source="appsettings.json" />
<!-- Update log file path with proper escaping for backslashes -->
<Json:JsonFile
Id="SetLogPath"
File="[#AppSettingsJson]"
ElementPath="$.Serilog.WriteTo[\[]0[\]].Args.path"
Value="[LOGFOLDER]app-.log"
Action="setValue" />
<!-- Update rolling interval -->
<Json:JsonFile
Id="SetLogRolling"
File="[#AppSettingsJson]"
ElementPath="$.Serilog.WriteTo[\[]0[\]].Args.rollingInterval"
Value="[LOG_ROLLING_INTERVAL]"
Action="setValue" />
</Component>
<!-- Define log folder property -->
<Property Id="LOGFOLDER" Value="C:\\ProgramData\\[Manufacturer]\\[ProductName]\\Logs\\" />
<Property Id="LOG_ROLLING_INTERVAL" Value="Day" />
Feature Flags
Conditioning JSON changes:
JsonFilehas no per-element condition. The MSI-idiomatic way to make a change conditional is to gate the component that carries it, using theConditionattribute on<Component>. When the condition is false the component is not installed, so its JSON operations do not run. A component that only carries JSON operations (no installed file) still needs a key path, so the examples below add a smallRegistryValuefor that purpose. The file itself is installed once by an unconditional component and referenced from the others via[#FileId].
Pattern: Toggle Feature Flags
Use Case: Enable or disable features based on installation options.
Example appsettings.json:
{
"Features": {
"EnableNewUI": false,
"EnableAdvancedReporting": false,
"EnableBetaFeatures": false
}
}
WiX Fragment:
<!-- The file is installed once, unconditionally -->
<Component Id="ConfigComponent" Guid="*">
<File Id="AppSettingsJson" Name="appsettings.json" Source="appsettings.json" />
</Component>
<!-- Enable new UI feature if selected -->
<Component Id="EnableNewUIComponent" Guid="*" Condition='FEATURE_NEW_UI = "1"'>
<RegistryValue Root="HKLM" Key="Software\MyApp\Json" Name="EnableNewUI" Type="integer" Value="1" KeyPath="yes" />
<Json:JsonFile
Id="EnableNewUIFeature"
File="[#AppSettingsJson]"
ElementPath="$.Features.EnableNewUI"
Value="true"
Action="setValue" />
</Component>
<!-- Enable advanced reporting if premium edition -->
<Component Id="EnableAdvancedReportingComponent" Guid="*" Condition='EDITION = "Premium"'>
<RegistryValue Root="HKLM" Key="Software\MyApp\Json" Name="EnableAdvancedReporting" Type="integer" Value="1" KeyPath="yes" />
<Json:JsonFile
Id="EnableAdvancedReporting"
File="[#AppSettingsJson]"
ElementPath="$.Features.EnableAdvancedReporting"
Value="true"
Action="setValue" />
</Component>
<!-- Feature selection properties -->
<Property Id="FEATURE_NEW_UI" Value="0" />
<Property Id="EDITION" Value="Standard" />
Pattern: Environment-Based Feature Flags
WiX Fragment:
<!-- The file is installed once, unconditionally -->
<Component Id="ConfigComponent" Guid="*">
<File Id="AppSettingsJson" Name="appsettings.json" Source="appsettings.json" />
</Component>
<!-- Enable beta features only in development -->
<Component Id="EnableBetaComponent" Guid="*" Condition='ENVIRONMENT = "Development"'>
<RegistryValue Root="HKLM" Key="Software\MyApp\Json" Name="EnableBeta" Type="integer" Value="1" KeyPath="yes" />
<Json:JsonFile
Id="EnableBetaFeatures"
File="[#AppSettingsJson]"
ElementPath="$.Features.EnableBetaFeatures"
Value="true"
Action="setValue" />
</Component>
<!-- Disable in production (ensure it's false) -->
<Component Id="DisableBetaComponent" Guid="*" Condition='ENVIRONMENT = "Production"'>
<RegistryValue Root="HKLM" Key="Software\MyApp\Json" Name="DisableBeta" Type="integer" Value="1" KeyPath="yes" />
<Json:JsonFile
Id="DisableBetaFeatures"
File="[#AppSettingsJson]"
ElementPath="$.Features.EnableBetaFeatures"
Value="false"
Action="setValue" />
</Component>
API Endpoints
Pattern: Configure API URLs
Use Case: Set API endpoint URLs based on environment or user input.
Example appsettings.json:
{
"ApiSettings": {
"BaseUrl": "https://api.example.com",
"Endpoints": {
"Users": "/api/v1/users",
"Products": "/api/v1/products"
},
"Timeout": 30
}
}
WiX Fragment:
<Component Id="ConfigComponent" Guid="*">
<File Id="AppSettingsJson" Name="appsettings.json" Source="appsettings.json" />
<!-- Set API base URL -->
<Json:JsonFile
Id="SetApiBaseUrl"
File="[#AppSettingsJson]"
ElementPath="$.ApiSettings.BaseUrl"
Value="[API_BASE_URL]"
Action="setValue" />
<!-- Set API timeout -->
<Json:JsonFile
Id="SetApiTimeout"
File="[#AppSettingsJson]"
ElementPath="$.ApiSettings.Timeout"
Value="[API_TIMEOUT]"
Action="setValue" />
</Component>
<!-- Default API settings -->
<Property Id="API_BASE_URL" Value="https://api.example.com" />
<Property Id="API_TIMEOUT" Value="30" />
<!-- Environment-specific overrides -->
<SetProperty Id="API_BASE_URL" Value="https://dev-api.example.com" After="AppSearch" Sequence="first">
<![CDATA[ENVIRONMENT = "Development"]]>
</SetProperty>
<SetProperty Id="API_BASE_URL" Value="https://staging-api.example.com" After="AppSearch" Sequence="first">
<![CDATA[ENVIRONMENT = "Staging"]]>
</SetProperty>
Environment-Specific Settings
Pattern: Complete Environment Configuration
Use Case: Configure all settings based on deployment environment.
Example appsettings.json:
{
"Environment": "Development",
"ConnectionStrings": {
"Default": "Server=localhost;Database=MyApp;"
},
"Logging": {
"LogLevel": {
"Default": "Information"
}
},
"Features": {
"DebugMode": false
}
}
WiX Fragment:
<!-- The file and the unconditional edit are installed together -->
<Component Id="ConfigComponent" Guid="*">
<File Id="AppSettingsJson" Name="appsettings.json" Source="appsettings.json" />
<!-- Set environment name (applies in every environment) -->
<Json:JsonFile
Id="SetEnvironment"
File="[#AppSettingsJson]"
ElementPath="$.Environment"
Value="[ENVIRONMENT]"
Action="setValue"
Sequence="1" />
</Component>
<!-- Development environment settings -->
<Component Id="DevSettingsComponent" Guid="*" Condition='ENVIRONMENT = "Development"'>
<RegistryValue Root="HKLM" Key="Software\MyApp\Json" Name="DevSettings" Type="integer" Value="1" KeyPath="yes" />
<Json:JsonFile
Id="DevConnectionString"
File="[#AppSettingsJson]"
ElementPath="$.ConnectionStrings.Default"
Value="Server=dev-db;Database=MyApp;"
Action="setValue"
Sequence="2" />
<Json:JsonFile
Id="DevLogLevel"
File="[#AppSettingsJson]"
ElementPath="$.Logging.LogLevel.Default"
Value="Debug"
Action="setValue"
Sequence="3" />
</Component>
<!-- Production environment settings -->
<Component Id="ProdSettingsComponent" Guid="*" Condition='ENVIRONMENT = "Production"'>
<RegistryValue Root="HKLM" Key="Software\MyApp\Json" Name="ProdSettings" Type="integer" Value="1" KeyPath="yes" />
<Json:JsonFile
Id="ProdConnectionString"
File="[#AppSettingsJson]"
ElementPath="$.ConnectionStrings.Default"
Value="[PROD_CONNECTION_STRING]"
Action="setValue"
Sequence="2" />
<Json:JsonFile
Id="ProdLogLevel"
File="[#AppSettingsJson]"
ElementPath="$.Logging.LogLevel.Default"
Value="Warning"
Action="setValue"
Sequence="3" />
</Component>
<!-- Environment property -->
<Property Id="ENVIRONMENT" Value="Production" />
<Property Id="PROD_CONNECTION_STRING" />
Complex Nested Configurations
Pattern: Deep Nesting with Arrays
Use Case: Configure complex nested settings like Serilog with multiple sinks.
Example appsettings.json:
{
"Serilog": {
"Using": ["Serilog.Sinks.Console", "Serilog.Sinks.File"],
"MinimumLevel": "Debug",
"WriteTo": [
{
"Name": "Console"
},
{
"Name": "File",
"Args": {
"path": "Logs/app.log",
"rollingInterval": "Day",
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}"
}
}
]
}
}
WiX Fragment:
<Component Id="ConfigComponent" Guid="*">
<File Id="AppSettingsJson" Name="appsettings.json" Source="appsettings.json" />
<!-- Update minimum level -->
<Json:JsonFile
Id="SetMinLevel"
File="[#AppSettingsJson]"
ElementPath="$.Serilog.MinimumLevel"
Value="[LOG_MIN_LEVEL]"
Action="setValue" />
<!-- Update file sink path (second item in WriteTo array) -->
<Json:JsonFile
Id="SetFileSinkPath"
File="[#AppSettingsJson]"
ElementPath="$.Serilog.WriteTo[\[]1[\]].Args.path"
Value="[LOGFOLDER]app.log"
Action="setValue" />
<!-- Update rolling interval -->
<Json:JsonFile
Id="SetRollingInterval"
File="[#AppSettingsJson]"
ElementPath="$.Serilog.WriteTo[\[]1[\]].Args.rollingInterval"
Value="[LOG_ROLLING_INTERVAL]"
Action="setValue" />
<!-- Update output template -->
<Json:JsonFile
Id="SetOutputTemplate"
File="[#AppSettingsJson]"
ElementPath="$.Serilog.WriteTo[\[]1[\]].Args.outputTemplate"
Value="{Timestamp:yyyy-MM-dd HH:mm:ss} [{Level}] {Message}{NewLine}{Exception}"
Action="setValue" />
</Component>
<Property Id="LOG_MIN_LEVEL" Value="Information" />
<Property Id="LOGFOLDER" Value="[CommonAppDataFolder][Manufacturer]\\[ProductName]\\Logs\\" />
<Property Id="LOG_ROLLING_INTERVAL" Value="Day" />
Array Manipulation
Pattern: Update Multiple Array Items
Use Case: Update all items in an array matching a condition.
Example appsettings.json:
{
"Servers": [
{
"Name": "WebServer1",
"Url": "http://localhost:5000",
"Enabled": true
},
{
"Name": "WebServer2",
"Url": "http://localhost:5001",
"Enabled": false
}
]
}
WiX Fragment:
<Component Id="ConfigComponent" Guid="*">
<File Id="AppSettingsJson" Name="appsettings.json" Source="appsettings.json" />
<!-- Update first server URL -->
<Json:JsonFile
Id="UpdateServer1Url"
File="[#AppSettingsJson]"
ElementPath="$.Servers[\[]0[\]].Url"
Value="[SERVER1_URL]"
Action="setValue" />
<!-- Update second server URL -->
<Json:JsonFile
Id="UpdateServer2Url"
File="[#AppSettingsJson]"
ElementPath="$.Servers[\[]1[\]].Url"
Value="[SERVER2_URL]"
Action="setValue" />
<!-- Update server by name using filter -->
<Json:JsonFile
Id="UpdateServerByName"
File="[#AppSettingsJson]"
ElementPath="$.Servers[\[]?(@.Name == 'WebServer1')[\]].Enabled"
Value="true"
Action="setValue" />
</Component>
<Property Id="SERVER1_URL" Value="http://localhost:5000" />
<Property Id="SERVER2_URL" Value="http://localhost:5001" />
Conditional Updates
Pattern: Conditional JSON Modifications
Use Case: Only update certain values if specific conditions are met.
WiX Fragment:
<!-- The file is installed once, unconditionally -->
<Component Id="ConfigComponent" Guid="*">
<File Id="AppSettingsJson" Name="appsettings.json" Source="appsettings.json" />
</Component>
<!-- Only update if user selected custom database -->
<Component Id="CustomDbComponent" Guid="*" Condition='USE_CUSTOM_DATABASE = "1" AND CUSTOM_DB_CONNECTION'>
<RegistryValue Root="HKLM" Key="Software\MyApp\Json" Name="CustomDb" Type="integer" Value="1" KeyPath="yes" />
<Json:JsonFile
Id="CustomDatabaseConnection"
File="[#AppSettingsJson]"
ElementPath="$.ConnectionStrings.Default"
Value="[CUSTOM_DB_CONNECTION]"
Action="setValue" />
</Component>
<!-- Configure HTTPS only if a certificate is provided -->
<Component Id="HttpsComponent" Guid="*" Condition='SSL_CERTIFICATE_PATH <> ""'>
<RegistryValue Root="HKLM" Key="Software\MyApp\Json" Name="Https" Type="integer" Value="1" KeyPath="yes" />
<Json:JsonFile
Id="EnableHttps"
File="[#AppSettingsJson]"
ElementPath="$.Kestrel.Endpoints.Https.Enabled"
Value="true"
Action="setValue" />
<Json:JsonFile
Id="SetCertPath"
File="[#AppSettingsJson]"
ElementPath="$.Kestrel.Endpoints.Https.Certificate.Path"
Value="[SSL_CERTIFICATE_PATH]"
Action="setValue" />
</Component>
Uninstall Clean-up
Pattern: Register with a Shared Configuration File and Unregister on Uninstall
Use Case: Your product adds itself to a JSON file it does not own - a host application’s plugin list, a machine-wide settings file under CommonAppDataFolder, a shared tool’s configuration - and must remove its entry again when uninstalled so the host is not left pointing at deleted files.
WiX Fragment:
<!-- Keyed on a registry value: this component installs no file of its own, it only edits the
host's file, which outlives this product. -->
<Component Id="HostRegistration" Guid="*">
<RegistryValue Root="HKLM" Key="Software\MyApp\Host" Name="Registered" Type="integer" Value="1" KeyPath="yes" />
<!-- Install / repair: add (or refresh) our entry -->
<Json:JsonFile
Id="RegisterWithHost"
File="[CommonAppDataFolder]HostApp\plugins.json"
ElementPath="/plugins/MyApp"
Value='{"path":"[INSTALLFOLDER]MyApp.Plugin.dll","enabled":true}'
Action="createJsonPointerValue" />
<!-- Uninstall: remove the entry. OnlyIfExists tolerates an administrator having removed it by
hand; without it a missing path fails the uninstall. -->
<Json:JsonFile
Id="UnregisterFromHost"
File="[CommonAppDataFolder]HostApp\plugins.json"
ElementPath="$.plugins.MyApp"
Action="deleteValue"
OnlyIfExists="yes"
On="uninstall" />
</Component>
How it behaves:
On="install"is the default, soRegisterWithHostruns at install and repair, afterInstallFiles.UnregisterFromHostruns only while the component is being uninstalled, beforeRemoveFiles, so the host file is edited while everything is still in place.- On a major upgrade the old version unregisters first, then the new version registers, so the entry ends up pointing at the new install.
Pattern: Hand a Shared File Back Exactly As It Was
Use Case: You modify a file you do not own and want it returned to its pre-install state on uninstall, whatever you changed and however many times the product was repaired. This is the simpler alternative to authoring the reverts by hand.
WiX Fragment:
<Component Id="HostRegistration" Guid="*">
<RegistryValue Root="HKLM" Key="Software\MyApp\Host" Name="Registered" Type="integer" Value="1" KeyPath="yes" />
<!-- The backup is taken before this (the first) change and restored, then removed, on uninstall -->
<Json:JsonFile Id="RegisterWithHost" File="[CommonAppDataFolder]HostApp\plugins.json"
ElementPath="/plugins/MyApp" Value='{"path":"[INSTALLFOLDER]MyApp.Plugin.dll"}'
Action="createJsonPointerValue" CreateBackup="yes" RestoreOnUninstall="yes" />
<Json:JsonFile Id="EnableHostFeature" File="[CommonAppDataFolder]HostApp\plugins.json"
ElementPath="$.features.pluginsEnabled" Value="true" Action="setValue" />
</Component>
Both changes are undone by the restore; no On="uninstall" elements are needed. Prefer the explicit reverts of the previous pattern when other software may legitimately edit the file between your install and uninstall, since a restore also discards those edits.
Pattern: Restore a Setting You Changed
Use Case: Your installer switches a setting in a shared file (say, the host’s default renderer) and should put the previous value back on uninstall.
WiX Fragment:
<Component Id="RendererSwitch" Guid="*">
<RegistryValue Root="HKLM" Key="Software\MyApp\Host" Name="Renderer" Type="integer" Value="1" KeyPath="yes" />
<!-- Remember the value that was there before we changed it. readValue runs in the immediate
phase, before any write, and the property is a formatted [reference] in the Value below. -->
<Json:JsonFile
Id="ReadOriginalRenderer"
File="[CommonAppDataFolder]HostApp\settings.json"
ElementPath="$.renderer"
DefaultValue="software"
Action="readValue"
Property="ORIGINAL_RENDERER" />
<!-- Install: switch to ours, keeping a note of the original alongside it -->
<Json:JsonFile Id="SetRenderer" File="[CommonAppDataFolder]HostApp\settings.json"
ElementPath="$.renderer" Value="myapp" Action="setValue" Sequence="1" />
<Json:JsonFile Id="NoteOriginalRenderer" File="[CommonAppDataFolder]HostApp\settings.json"
ElementPath="/rendererBeforeMyApp" Value="[ORIGINAL_RENDERER]" Action="createJsonPointerValue" Sequence="2" />
<!-- Uninstall: read the note back and restore it, then drop the note -->
<Json:JsonFile
Id="ReadNotedRenderer"
File="[CommonAppDataFolder]HostApp\settings.json"
ElementPath="$.rendererBeforeMyApp"
DefaultValue="software"
Action="readValue"
Property="NOTED_RENDERER"
On="uninstall" />
<Json:JsonFile Id="RestoreRenderer" File="[CommonAppDataFolder]HostApp\settings.json"
ElementPath="$.renderer" Value="[NOTED_RENDERER]" Action="setValue" On="uninstall" Sequence="3" />
<Json:JsonFile Id="DropRendererNote" File="[CommonAppDataFolder]HostApp\settings.json"
ElementPath="$.rendererBeforeMyApp" Action="deleteValue" OnlyIfExists="yes" On="uninstall" Sequence="4" />
</Component>
Declare ORIGINAL_RENDERER and NOTED_RENDERER as properties (<Property Id="NOTED_RENDERER" Value="software" />) so ICE checks see them and a missing note still restores something sensible.
Best Practices
1. Use Sequence Attribute
When making multiple changes, use the Sequence attribute to control the order of operations:
<Json:JsonFile Id="CreateSection" ... Sequence="1" />
<Json:JsonFile Id="UpdateValue" ... Sequence="2" />
2. Escape Square Brackets
Always escape square brackets in JSONPath expressions for MSI:
<!-- Wrong -->
ElementPath="$.Books[0].Title"
<!-- Correct -->
ElementPath="$.Books[\[]0[\]].Title"
3. Use File References
Reference files using [#FileId] instead of hardcoding paths:
<File Id="AppConfig" Name="appsettings.json" Source="appsettings.json" />
<Json:JsonFile File="[#AppConfig]" ... />
4. Validate Property Names
Property names must be uppercase and set before use:
<Property Id="MY_VALUE" Value="Default" />
<Json:JsonFile Value="[MY_VALUE]" ... />
5. Use Conditions for Optional Updates
JsonFile has no per-element condition. Gate the component that carries the change with the
Condition attribute on <Component> (a condition-only component needs its own key path):
<Component Id="OptionalUpdate" Guid="*" Condition='PROPERTY_NAME <> ""'>
<RegistryValue Root="HKLM" Key="Software\MyApp\Json" Name="OptionalUpdate" Type="integer" Value="1" KeyPath="yes" />
<Json:JsonFile Id="OptionalUpdate" File="[#AppSettingsJson]" ElementPath="$.Some.Value" Value="[PROPERTY_NAME]" Action="setValue" />
</Component>
6. Create Before Update
Use createJsonPointerValue to create paths that might not exist:
<Json:JsonFile
ElementPath="/NewSection/NewValue"
Value="MyValue"
Action="createJsonPointerValue" />
7. Handle Backslashes in Paths
Double backslashes for Windows paths:
Value="C:\\Program Files\\MyApp\\config.json"
Debugging Tips
View MSI Log
Generate a detailed log file to troubleshoot JSON file operations:
msiexec /i YourInstaller.msi /l*v install.log
Dry Run, Verbose Snapshots and the Transform Log
msiexec /i MyApp.msi /qn /l*v install.log JSONEXT_DRYRUN=1 JSONEXT_TRANSFORMLOG=C:\temp\json-ops.json
msiexec /i MyApp.msi /qn /l*v install.log JSONEXT_LOGLEVEL=verbose
The first command applies nothing and writes one JSON record per operation, with the value found at each path; the second logs the value before and after every operation in the MSI log. See the README’s Diagnostics section.
Try a Path Outside MSI
jsoncli readValue appsettings.json $.ConnectionStrings.Default
jsoncli setValue appsettings.json $.Logging.LogLevel.Default Warning --dry-run
tools\jsoncli.exe from the NuGet package runs the custom action’s transform code directly against a file.
Search for JSON Operations
Look for these patterns in the log:
ExecJsonFile- Custom action executionConfiguring JSON file- File being processedElement path:- The JSONPath being usedSetting JSON value- Value operationsErrororFailed- Error messages
Test JSONPath Expressions
Use online JSONPath evaluators to test your expressions:
- https://jsonpath.com/
- https://jsonpath.curiousconcept.com/
Common Errors and Solutions
Error: “Element not found”
- Check JSONPath syntax
- Verify the path exists in your JSON
- Ensure square brackets are escaped:
[\[]and[\]]
Error: “Invalid file path”
- Verify file reference:
[#FileId] - Check file is in the same component
- Ensure file is installed before modification
Property not expanded
- Property must be uppercase
- Property must be set before JSON action executes
- Use brackets:
[PROPERTY_NAME]