I see a lot of answers use Newtonsoft.Json
package to update appsettings. I will provide some some solutions that use System.Text.Json
package (built-in on .Net Core 3 and above).
OPTION 1
Before you start updating appsettings.json
file dynamically, ask yourself a question, how comlex is that part of appsettings.json that needs to be updated. If the part that needs to be updated is not very complex, you can use appsettings transformation functionality just for that part that that needs to be updated. Here's an example:
Let's say my appsettings.json
file looks like that:
{
"Username": "Bro300",
"Job": {
"Title": "Programmer",
"Type": "IT"
}
}
And let's say I need to update only Job
section. Instead of updating appsettings.json
directly I can create a smaller file appsettings.MyOverrides.json
that will look like this:
{
"Job": {
"Title": "Farmer",
"Type": "Agriculture"
}
}
And then make sure that this new file is added in my .Net Core app, and .Net Core will figure out how to load the new updated settings.
Now the next step is to create a wrapper class that will hold values from appsettings.MyOverrides.json
like this:
public class OverridableSettings
{
public JobSettings Job { get; set; }
}
public class JobSettings
{
public string Title { get; set; }
public string Type { get; set; }
}
And then I can create my updater class that will look like this (notice that it takes in OverridableSettings
and completely overrides appsettings.MyOverrides.json
file:
public class AppSettingsUpdater
{
public void UpdateSettings(OverridableSettings settings)
{
// instead of updating appsettings.json file directly I will just write the part I need to update to appsettings.MyOverrides.json
// .Net Core in turn will read my overrides from appsettings.MyOverrides.json file
const string SettinsgOverridesFileName = "appsettings.MyOverrides.json";
var newConfig = JsonSerializer.Serialize(settings, new JsonSerializerOptions { WriteIndented = true });
File.WriteAllText(SettinsgOverridesFileName, newConfig);
}
}
Finally this is the code that demonstrates how to use it:
public static class Program
{
public static void Main()
{
// Notice that appsettings.MyOverrides.json will contain only the part that we need to update, other settings will live in appsettings.json
// Also appsettings.MyOverrides.json is optional so if it doesn't exist at the program start it's not a problem
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddJsonFile("appsettings.MyOverrides.json", optional: true)
.Build();
// Here we read our current settings
var settings = configuration.Get<OverridableSettings>();
var settingsUpdater = new AppSettingsObjectUpdater();
settings.Job.Title = "Farmer";
settings.Job.Type = "Agriculture";
settingsUpdater.UpdateSettings(settings);
// Here we reload the settings so the new values from appsettings.MyOverrides.json will be read
configuration.Reload();
// and here we retrieve the new updated settings
var newJobSettings = configuration.GetSection("Job").Get<JobSettings>();
}
}
OPTION 2
If the appsetting transformation does not fit you case, and you have to update values only one level deep, you can use this simple implementation:
public void UpdateAppSetting(string key, string value)
{
var configJson = File.ReadAllText("appsettings.json");
var config = JsonSerializer.Deserialize<Dictionary<string, object>>(configJson);
config[key] = value;
var updatedConfigJson = JsonSerializer.Serialize(config, new JsonSerializerOptions { WriteIndented = true });
File.WriteAllText("appsettings.json", updatedConfigJson);
}
OPTION 3
Finally, if you have some complex case and you need to update appsettings, multiple levels deep, here is another implementation, that expands on the previous option, and uses recursion to update the settings at any level:
public class AppSettingsUpdater
{
private const string EmptyJson = "{}";
public void UpdateAppSetting(string key, object value)
{
// Empty keys "" are allowed in json by the way
if (key == null)
{
throw new ArgumentException("Json property key cannot be null", nameof(key));
}
const string settinsgFileName = "appsettings.json";
// We will create a new file if appsettings.json doesn't exist or was deleted
if (!File.Exists(settinsgFileName))
{
File.WriteAllText(settinsgFileName, EmptyJson);
}
var config = File.ReadAllText(settinsgFileName);
var updatedConfigDict = UpdateJson(key, value, config);
// After receiving the dictionary with updated key value pair, we serialize it back into json.
var updatedJson = JsonSerializer.Serialize(updatedConfigDict, new JsonSerializerOptions { WriteIndented = true });
File.WriteAllText(settinsgFileName, updatedJson);
}
// This method will recursively read json segments separated by semicolon (firstObject:nestedObject:someProperty)
// until it reaches the desired property that needs to be updated,
// it will update the property and return json document represented by dictonary of dictionaries of dictionaries and so on.
// This dictionary structure can be easily serialized back into json
private Dictionary<string, object> UpdateJson(string key, object value, string jsonSegment)
{
const char keySeparator = ':';
var config = JsonSerializer.Deserialize<Dictionary<string, object>>(jsonSegment);
var keyParts = key.Split(keySeparator);
var isKeyNested = keyParts.Length > 1;
if (isKeyNested)
{
var firstKeyPart = keyParts[0];
var remainingKey = string.Join(keySeparator, keyParts.Skip(1));
// If the key does not exist already, we will create a new key and append it to the json
var newJsonSegment = config.ContainsKey(firstKeyPart) && config[firstKeyPart] != null
? config[firstKeyPart].ToString()
: EmptyJson;
config[firstKeyPart] = UpdateJson(remainingKey, value, newJsonSegment);
}
else
{
config[key] = value;
}
return config;
}
}
You can use, like this:
var settingsUpdater = new AppSettingsUpdater();
settingsUpdater.UpdateAppSetting("OuterProperty:NestedProperty:PropertyToUpdate", "new value");