Skip to the content.

WixJsonFileExtension Cookbook

This cookbook provides practical examples and patterns for common JSON configuration scenarios using WixJsonFileExtension with WiX 5+.

Table of Contents

  1. Connection Strings
  2. Logging Configuration
  3. Feature Flags
  4. API Endpoints
  5. Environment-Specific Settings
  6. Complex Nested Configurations
  7. Array Manipulation
  8. Conditional Updates
  9. 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: JsonFile has no per-element condition. The MSI-idiomatic way to make a change conditional is to gate the component that carries it, using the Condition attribute 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 small RegistryValue for 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 &lt;&gt; ""'>
  <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:

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 &lt;&gt; ""'>
  <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:

Test JSONPath Expressions

Use online JSONPath evaluators to test your expressions:

Common Errors and Solutions

Error: “Element not found”

Error: “Invalid file path”

Property not expanded


Additional Resources