C# – How to update appsettings.json programmatically

You have to overwrite the appsettings.json file to be able to update values programmatically. You have to deal with the whole file, not individual parts of it. The process can be summarized in the following steps:

  • Load appsettings.json and deserialize it into an object.
  • Update properties on the object.
  • Serialize the object into a JSON string and overwrite appsettings.json with it.

There are two options for deserialization. You can either 1) Deserialize appsettings.json into a dynamic object or 2) Load appsettings.json with ConfigurationBuilder into a config class. In this article, I’ll show how to do these two approaches to update existing properties in appsettings.json. At the end, I’ll show how to insert a new property using the dynamic approach.

Initial appsettings.json for reference

In all examples in this article, the initial appsettings.json file will contain the following JSON:

{
  "DebugEnabled": false,
  "WeatherClientConfig": {
    "IsEnabled": true,
    "WeatherAPIUrl": "https://localhost:12345",
    "Timeout": 5000,
    "TemperatureUnits": "Kelvin"
  }
}
Code language: JSON / JSON with Comments (json)

Approach 1 – Load appsettings.json into a dynamic object

This approach deserializes appsettings.json into a dynamic object using Newtonsoft. Values are changed, and then persisted by serializing the dynamic object and overwriting appsettings.json.

This uses Newtonsoft because it works better than the built-in System.Text.Json serializer when it comes to deserializing dynamic objects.

Because this is deserializing to a dynamic object, it can’t use ConfigurationBuilder to load appsettings.json. Instead, it uses File.ReadAllText(). The downside of this approach is that it won’t load in values from user secrets.

Install Newtonsoft

If you don’t already have Newtonsoft, install the nuget package (this is using View > Other Windows > Package Manager Console):

Install-Package Newtonsoft.Json
Code language: PowerShell (powershell)

Step 1 – Load appsettings.json and deserialize into a dynamic object

The first step is to load appsettings.json from the current working directory by using File.ReadAllText():

var appSettingsPath = Path.Combine(System.IO.Directory.GetCurrentDirectory(), "appsettings.json");
var json = File.ReadAllText(appSettingsPath);
Code language: C# (cs)

Deserialize this JSON string into a dynamic object with Newtonsoft like this:

var jsonSettings = new JsonSerializerSettings();
jsonSettings.Converters.Add(new ExpandoObjectConverter());
jsonSettings.Converters.Add(new StringEnumConverter());

dynamic config = JsonConvert.DeserializeObject<ExpandoObject>(json, jsonSettings);
Code language: C# (cs)

At a bare minimum you have to use ExpandoObjectConverter. Pass in whatever additional serialization settings you need. In this example, it’s using StringEnumConverter so that it shows enum names instead of numeric values.

Step 2 – Change values

Change the values on the dynamic object as desired:

config.DebugEnabled = true;
config.WeatherClientConfig.TemperatureUnits = TemperatureUnits.Fahrenheit;
Code language: C# (cs)

In this example, let’s say you have a user interface that allows the user to pass in these two settings, so the code only needs to deal with updating these.

Step 3 – Serialize the dynamic object and overwrite appsettings.json

Now serialize the dynamic object, using the desired settings.

var newJson = JsonConvert.SerializeObject(config, Formatting.Indented, jsonSettings);
Code language: C# (cs)

Note: Most people will want the JSON in appsettings.json to be indented for readability, so be sure to pass in Formatting.Indented.

And finally overwrite appsettings.json with the new JSON:

File.WriteAllText(appSettingsPath, newJson);
Code language: C# (cs)

Full example

Putting this all together, we have the following code:

using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

var appSettingsPath = Path.Combine(System.IO.Directory.GetCurrentDirectory(), "appsettings.json");
var json = File.ReadAllText(appSettingsPath);

var jsonSettings = new JsonSerializerSettings();
jsonSettings.Converters.Add(new ExpandoObjectConverter());
jsonSettings.Converters.Add(new StringEnumConverter());

dynamic config = JsonConvert.DeserializeObject<ExpandoObject>(json, jsonSettings);

config.DebugEnabled = true;
config.WeatherClientConfig.TemperatureUnits = TemperatureUnits.Fahrenheit;

var newJson = JsonConvert.SerializeObject(config, Formatting.Indented, jsonSettings);

File.WriteAllText(appSettingsPath, newJson);
Code language: C# (cs)

Running this results in the two settings getting updated in appsettings.json:

{
  "DebugEnabled": true,
  "WeatherClientConfig": {
    "IsEnabled": true,
    "WeatherAPIUrl": "https://localhost:12345",
    "Timeout": 5000,
    "TemperatureUnits": "Fahrenheit"
  }
}
Code language: JSON / JSON with Comments (json)

Approach 2 – Load appsettings.json with ConfigurationBuilder into a config class

This approach uses ConfigurationBuilder to load appsettings.json into a config class. Values can then be changed on the config object and persisted by overwriting appsettings.json with the serialized config object.

This uses the built-in System.Text.Json serializer.

If you’re using user secrets, ConfigurationBuilder will load the values from the secrets file.

Install ConfigurationBuilder extension methods for JSON

If you don’t already have these ConfigurationBuilder extension methods, install the nuget packages (this is using View > Other Windows > Package Manager Console):

Install-Package Microsoft.Extensions.Configuration.Binder
Install-Package Microsoft.Extensions.Configuration.Json
Code language: PowerShell (powershell)

Step 1 – Add a config class

Add a config class (and supporting classes) that represents all the properties found in appsettings.json. In this example, the following three entities are needed:

public class Config
{
	public bool DebugEnabled { get; set; }
	public WeatherClientConfig WeatherClientConfig { get; set; }
}

public class WeatherClientConfig
{
	public bool IsEnabled { get; set; }
	public string WeatherAPIUrl { get; set; }
	public int Timeout { get; set; }

	public TemperatureUnits TemperatureUnits { get; set; }  
}

public enum TemperatureUnits
{
	Kelvin,
	Fahrenheit,
	Celsius
}
Code language: C# (cs)

Step 2 – Load appsettings.json into the config object

Use ConfigurationBuilder to load appsettings.json, like this:

using Microsoft.Extensions.Configuration;

var config = new ConfigurationBuilder()
            .SetBasePath(AppDomain.CurrentDomain.BaseDirectory)
            .AddJsonFile("appsettings.json")
            .Build()
            .Get<Config>();
Code language: C# (cs)

This deserializes appsettings.json into the config object shown in the previous step.

Step 3 – Change values

Change the values in the config object as desired:

config.WeatherClientConfig.Timeout = 1000;
config.WeatherClientConfig.TemperatureUnits = TemperatureUnits.Celsius;
Code language: C# (cs)

Step 4 – Serialize the config object and overwrite appsettings.json

Use the built-in System.Text.Json serializer to serialize the config object. Use the desired serialization settings. You’ll probably want the JSON indented in appsettings.json, so set WriteIndented=true:

var jsonWriteOptions = new JsonSerializerOptions()
{
	WriteIndented = true
};
jsonWriteOptions.Converters.Add(new JsonStringEnumConverter());

var newJson = JsonSerializer.Serialize(config, jsonWriteOptions);
Code language: C# (cs)

Now overwrite appsettings.json with the new JSON:

var appSettingsPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "appsettings.json");
File.WriteAllText(appSettingsPath, newJson);
Code language: C# (cs)

Full example

Pulling all of this together, we have the following code:

using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.Extensions.Configuration;

var config = new ConfigurationBuilder()
	.SetBasePath(AppDomain.CurrentDomain.BaseDirectory)
	.AddJsonFile("appsettings.json")
	.Build()
	.Get<Config>();

config.WeatherClientConfig.Timeout = 1000;
config.WeatherClientConfig.TemperatureUnits = TemperatureUnits.Celsius;

var jsonWriteOptions = new JsonSerializerOptions()
{
	WriteIndented = true
};
jsonWriteOptions.Converters.Add(new JsonStringEnumConverter());

var newJson = JsonSerializer.Serialize(config, jsonWriteOptions);

var appSettingsPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "appsettings.json");
File.WriteAllText(appSettingsPath, newJson);
Code language: C# (cs)

Running this results in the two settings getting updated in appsettings.json:

{
  "DebugEnabled": false,
  "WeatherClientConfig": {
    "IsEnabled": true,
    "WeatherAPIUrl": "https://localhost:12345",
    "Timeout": 1000,
    "TemperatureUnits": "Celsius"
  }
}
Code language: JSON / JSON with Comments (json)

Be aware of how this functions with user secrets

There are a few things to be aware of it you’re using user secrets with the ConfigurationBuilder approach.

Values from the secrets file will end up in appsettings.json

This happens because ConfigurationBuilder will pull values from the secrets file and put them in the config object, which is then serialized and used to overwrite appsettings.json. This is probably not a big deal, because this is updating values in the deployed appsettings.json file (not in the appsettings.json that’s part of the project source files).

For example, let’s say your appsettings.json looks like this:

{
  "DebugEnabled": false,
  "WeatherClientConfig": {
    "IsEnabled": true,
    "WeatherAPIUrl": "https://localhost:12345",
    "Timeout": 5000,
    "TemperatureUnits": "Kelvin"
  },
  "Password":  ""
}
Code language: JSON / JSON with Comments (json)

And the Password property is stored in a user secrets file:

{
  "Password":  "hi"
}Code language: JSON / JSON with Comments (json)

When you overwrite the appsettings.json file with the serialized config object, it’ll end up like this:

{
  "DebugEnabled": false,
  "WeatherClientConfig": {
    "IsEnabled": true,
    "WeatherAPIUrl": "https://localhost:12345",
    "Timeout": 1000,
    "TemperatureUnits": "Celsius"
  },
  "Password": "hi"
}
Code language: JSON / JSON with Comments (json)

Notice the password value from the secrets file ended up in appsettings.json.

Values in the secrets file will continue to override values in appsettings.json

This process is only programmatically updating appsettings.json. It’s not updating the secrets file. This means the values in the secrets file will continue to override values in appsettings.json. This is expected behavior if you’re using user secrets, but it can be surprising.

This would be a problem if you’re programmatically updating a value that is already being overridden by the user secrets. Here’s an example. Let’s say your appsettings.json looks like this:

{
  "DebugEnabled": false,
  "WeatherClientConfig": {
    "IsEnabled": true,
    "WeatherAPIUrl": "https://localhost:12345",
    "Timeout": 5000,
    "TemperatureUnits": "Kelvin"
  },
  "Password":  ""
}
Code language: JSON / JSON with Comments (json)

And the secrets file is overriding the Password field:

{
  "Password":  "hi"
}
Code language: JSON / JSON with Comments (json)

Now let’s say you’re programmatically updating the password to “Bye” and persisting it to appsettings.json, which would look like this:

{
  "DebugEnabled": false,
  "WeatherClientConfig": {
    "IsEnabled": true,
    "WeatherAPIUrl": "https://localhost:12345",
    "Timeout": 1000,
    "TemperatureUnits": "Celsius"
  },
  "Password": "Bye"
}
Code language: JSON / JSON with Comments (json)

The next time the config is loaded, what will the Password value be?

var config = new ConfigurationBuilder()
	.SetBasePath(AppDomain.CurrentDomain.BaseDirectory)
	.AddJsonFile("appsettings.json")
	.AddUserSecrets<Program>()
	.Build()
	.Get<Config>();

Console.WriteLine($"Password={config.Password}");
Code language: C# (cs)

Here’s what this outputs:

Password=hiCode language: plaintext (plaintext)

It’s the password value from the secrets file. It overrode the password value in appsettings.json, which is exactly what the user secrets feature is supposed to do. Even though this works as expected, you may want to avoid confusion by not trying to programmatically update values that will be overridden by the secrets file.

Adding a new property programmatically

Let’s say you want to add a new property to appsettings.json. In that case, you should definitely use the dynamic approach.

When you use the dynamic approach, you get an ExpandoObject. You can cast this as an IDictionary<string, object> object and add a property, like this:

using System.Dynamic;

var expando = config as IDictionary<string, object>;
expando.Add("Updated", DateTime.Now);
Code language: C# (cs)

When this is serialized and used to overwrite appsettings.json, the new property will be there.

Full example

This example uses the dynamic approach shown earlier in the article. The only difference here is that it’s adding a new property instead of updating an existing one.

using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
using System.Dynamic;

var appSettingsPath = Path.Combine(System.IO.Directory.GetCurrentDirectory(), "appsettings.json");
var json = File.ReadAllText(appSettingsPath);

var jsonSettings = new JsonSerializerSettings();
jsonSettings.Converters.Add(new ExpandoObjectConverter());
jsonSettings.Converters.Add(new StringEnumConverter());

dynamic config = JsonConvert.DeserializeObject<ExpandoObject>(json, jsonSettings);

var expando = config as IDictionary<string, object>;
expando.Add("Updated", DateTime.Now);

var newJson = JsonConvert.SerializeObject(config, Formatting.Indented, jsonSettings);

File.WriteAllText(appSettingsPath, newJson);
Code language: C# (cs)

After running this, the appsettings.json file has the new property:

{
  "DebugEnabled": false,
  "WeatherClientConfig": {
    "IsEnabled": true,
    "WeatherAPIUrl": "https://localhost:12345",
    "Timeout": 5000,
    "TemperatureUnits": "Kelvin"
  },
  "Updated": "2021-09-13T11:53:14.2549161-04:00"
}
Code language: JSON / JSON with Comments (json)

Comments are closed.