How to update values into appsetting.json?
Asked Answered
L

9

61

I am using the IOptions pattern as described in the official documentation.

This works fine when I am reading values from appsetting.json, but how do I update values and save changes back to appsetting.json?

In my case, I have a few fields that can be edited from the user interface (by admin user in application). Hence I am looking for the ideal approach to update these values via the option accessor.

Laevorotatory answered 5/12, 2016 at 9:26 Comment(6)
The framework provides a common infrastructure for reading configuration values, not to modify them. When it comes to modifying, you'll have to use the specific configuration provider to access and modify the underlying configuration source.Lakshmi
"specific configuration provider to access and modify the underlying configuration source"? Could you please give me some reference to start with?Laevorotatory
What is the configuration source you're intending to modify?Lakshmi
Like I said in my post - appsetting.json, In this I have few application wide settings stored which I intend to modify from UI.Laevorotatory
Is it an MVC application?Aurore
yes...it is ASP.NET Core MVC application.Laevorotatory
R
63

At the time of writing this answer it seemed that there is no component provided by the Microsoft.Extensions.Options package that has functionality to write configuration values back to appsettings.json.

In one of my ASP.NET Core projects I wanted to enable the user to change some application settings - and those setting values should be stored in appsettings.json, more precisly in an optional appsettings.custom.json file, that gets added to the configuration if present.

Like this...

public Startup(IHostingEnvironment env)
{
    IConfigurationBuilder builder = new ConfigurationBuilder()
        .SetBasePath(env.ContentRootPath)
        .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
        .AddJsonFile("appsettings.custom.json", optional: true, reloadOnChange: true)
        .AddEnvironmentVariables();

    this.Configuration = builder.Build();
}

I declared the IWritableOptions<T> interface that extends IOptions<T>; so I can just replace IOptions<T> by IWritableOptions<T> whenever I want to read and write settings.

public interface IWritableOptions<out T> : IOptions<T> where T : class, new()
{
    void Update(Action<T> applyChanges);
}

Also, I came up with IOptionsWriter, which is a component that is intended to be used by IWritableOptions<T> to update a configuration section. This is my implementation for the beforementioned interfaces...

class OptionsWriter : IOptionsWriter
{
    private readonly IHostingEnvironment environment;
    private readonly IConfigurationRoot configuration;
    private readonly string file;

    public OptionsWriter(
        IHostingEnvironment environment, 
        IConfigurationRoot configuration, 
        string file)
    {
        this.environment = environment;
        this.configuration = configuration;
        this.file = file;
    }

    public void UpdateOptions(Action<JObject> callback, bool reload = true)
    {
        IFileProvider fileProvider = this.environment.ContentRootFileProvider;
        IFileInfo fi = fileProvider.GetFileInfo(this.file);
        JObject config = fileProvider.ReadJsonFileAsObject(fi);
        callback(config);
        using (var stream = File.OpenWrite(fi.PhysicalPath))
        {
            stream.SetLength(0);
            config.WriteTo(stream);
        }

        this.configuration.Reload();
    }
}

Since the writer is not aware about the file structure, I decided to handle sections as JObject objects. The accessor tries to find the requested section and deserializes it to an instance of T, uses the current value (if not found), or just creates a new instance of T, if the current value is null. This holder object is than passed to the caller, who will apply the changes to it. Than the changed object gets converted back to a JToken instance that is going to replace the section...

class WritableOptions<T> : IWritableOptions<T> where T : class, new()
{
    private readonly string sectionName;
    private readonly IOptionsWriter writer;
    private readonly IOptionsMonitor<T> options;

    public WritableOptions(
        string sectionName, 
        IOptionsWriter writer, 
        IOptionsMonitor<T> options)
    {
        this.sectionName = sectionName;
        this.writer = writer;
        this.options = options;
    }

    public T Value => this.options.CurrentValue;

    public void Update(Action<T> applyChanges)
    {
        this.writer.UpdateOptions(opt =>
        {
            JToken section;
            T sectionObject = opt.TryGetValue(this.sectionName, out section) ?
                JsonConvert.DeserializeObject<T>(section.ToString()) :
                this.options.CurrentValue ?? new T();

            applyChanges(sectionObject);

            string json = JsonConvert.SerializeObject(sectionObject);
            opt[this.sectionName] = JObject.Parse(json);
        });
    }
}

Finally, I implemented an extension method for IServicesCollection allowing me to easily configure a writable options accessor...

static class ServicesCollectionExtensions
{
    public static void ConfigureWritable<T>(
        this IServiceCollection services, 
        IConfigurationRoot configuration, 
        string sectionName, 
        string file) where T : class, new()
    {
        services.Configure<T>(configuration.GetSection(sectionName));

        services.AddTransient<IWritableOptions<T>>(provider =>
        {
            var environment = provider.GetService<IHostingEnvironment>();
            var options = provider.GetService<IOptionsMonitor<T>>();
            IOptionsWriter writer = new OptionsWriter(environment, configuration, file);
            return new WritableOptions<T>(sectionName, writer, options);
        });
    }
}

Which can be used in ConfigureServices like...

services.ConfigureWritable<CustomizableOptions>(this.Configuration, 
    "MySection", "appsettings.custom.json");

In my Controller class I can just demand an IWritableOptions<CustomizableOptions> instance, that has the same characteristics as IOptions<T>, but also allows to change and store configuration values.

private IWritableOptions<CustomizableOptions> options;

...

this.options.Update((opt) => {
    opt.SampleOption = "...";
});
Riane answered 9/3, 2017 at 21:34 Comment(6)
Thank you very much. I have a follow up question regarding IConfigurationRoot, maybe you know the answer :)Shelve
Is this still the only way, one year later?Kimbra
@JavierGarcíaManzano I think so; there´s an issue on GitHub (filed in March 2018), but it has been closed: github.com/aspnet/Home/issues/2973Riane
So before I go down this path, I just wanted to check... Almost 4 years later and we still have to do something like this? Is there not something baked in by now? What are people using to write to custom JSON option/config files in 2021?Bootless
@ArvoBowen I think those people implement their own persisters that suit their needs. IMHO there is no generic solution that perfectly unbinds configuration and routes changes back to its source(s). If you only work with JSON-files you can go with the suggested solution, but if you combine several sources (maybe involving user-secrets, or environment variables), you may consider another solution to not put security at risk.Riane
Thanks, @Matze. I don't mind making a new class to handle all my settings in general. I tend to go out of my way to do stuff like that usually... Only to then later find out there is a much easier way to get the same task done. Trying to evolve my coding practice and look for something already built first. I was looking at Derrell's NuGet package in the comments on this question (https://mcmap.net/q/321986/-how-to-update-values-into-appsetting-json). It seems after all the work he put into it, he decided, in the end, doing that with JSON was too unstable I guess. So it gave me pause to ask the question.Bootless
A
57

Simplified version of Matze's answer:

public interface IWritableOptions<out T> : IOptionsSnapshot<T> where T : class, new()
{
    void Update(Action<T> applyChanges);
}

public class WritableOptions<T> : IWritableOptions<T> where T : class, new()
{
    private readonly IHostingEnvironment _environment;
    private readonly IOptionsMonitor<T> _options;
    private readonly string _section;
    private readonly string _file;

    public WritableOptions(
        IHostingEnvironment environment,
        IOptionsMonitor<T> options,
        string section,
        string file)
    {
        _environment = environment;
        _options = options;
        _section = section;
        _file = file;
    }

    public T Value => _options.CurrentValue;
    public T Get(string name) => _options.Get(name);

    public void Update(Action<T> applyChanges)
    {
        var fileProvider = _environment.ContentRootFileProvider;
        var fileInfo = fileProvider.GetFileInfo(_file);
        var physicalPath = fileInfo.PhysicalPath;

        var jObject = JsonConvert.DeserializeObject<JObject>(File.ReadAllText(physicalPath));
        var sectionObject = jObject.TryGetValue(_section, out JToken section) ?
            JsonConvert.DeserializeObject<T>(section.ToString()) : (Value ?? new T());

        applyChanges(sectionObject);

        jObject[_section] = JObject.Parse(JsonConvert.SerializeObject(sectionObject));
        File.WriteAllText(physicalPath, JsonConvert.SerializeObject(jObject, Formatting.Indented));
    }
}

public static class ServiceCollectionExtensions
{
    public static void ConfigureWritable<T>(
        this IServiceCollection services,
        IConfigurationSection section,
        string file = "appsettings.json") where T : class, new()
    {
        services.Configure<T>(section);
        services.AddTransient<IWritableOptions<T>>(provider =>
        {
            var environment = provider.GetService<IHostingEnvironment>();
            var options = provider.GetService<IOptionsMonitor<T>>();
            return new WritableOptions<T>(environment, options, section.Key, file);
        });
    }
}

Usage:

services.ConfigureWritable<MyOptions>(Configuration.GetSection("MySection"));

Then:

private readonly IWritableOptions<MyOptions> _options;

public MyClass(IWritableOptions<MyOptions> options)
{
    _options = options;
}

To save the changes to the file:

_options.Update(opt => {
    opt.Field1 = "value1";
    opt.Field2 = "value2";
});

And you can pass a custom json file as optional parameter (it will use appsettings.json by default):

services.ConfigureWritable<MyOptions>(Configuration.GetSection("MySection"), "appsettings.custom.json");
Andro answered 31/8, 2017 at 16:56 Comment(5)
I was a bit unsure of "MyOptions", but reading this helped me figure it out. codingblast.com/…Bloom
Have created a github repo and pushed as nuget package - with some changes for further flexibility: github.com/dazinator/Dazinator.Extensions.WritableOptionsHazan
Exellect solution. But while using it my code I encountered an error because of line: JObject.Parse(JsonConvert.SerializeObject(sectionObject)). This line will fail if sectionObject is an array. It can be replaced with: JToken.Parse(JsonConvert.SerializeObject(sectionObject))Instrumentation
This solution is great but I have one problem. My updated file is not injected after redirection (old configuration is injected). I've created question with explanation https://mcmap.net/q/324016/-why-is-not-injected-last-version-of-configuration-appsettings-json/9684060Rachael
Should IWritableOptions be a singleton instead of a transient?Restharrow
M
12

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");
Methylamine answered 10/6, 2021 at 8:7 Comment(1)
I tried option 3 it did not compile....Robillard
L
8
public static void SetAppSettingValue(string key, string value, string appSettingsJsonFilePath = null) {
 if (appSettingsJsonFilePath == null) {
  appSettingsJsonFilePath = System.IO.Path.Combine(System.AppContext.BaseDirectory, "appsettings.json");
 }

 var json = System.IO.File.ReadAllText(appSettingsJsonFilePath);
 dynamic jsonObj = Newtonsoft.Json.JsonConvert.DeserializeObject < Newtonsoft.Json.Linq.JObject > (json);

 jsonObj[key] = value;

 string output = Newtonsoft.Json.JsonConvert.SerializeObject(jsonObj, Newtonsoft.Json.Formatting.Indented);

 System.IO.File.WriteAllText(appSettingsJsonFilePath, output);
}
Layoff answered 3/12, 2019 at 18:29 Comment(1)
appsettings.json does not have to be valid json. For example in appsettings.json comments are allowed. Also dynamic ist extremly slow.Vulnerable
A
4

While there is still not a way via the Options accessor, I'd like to preset a .NET 6 class that makes it quite easy to write back to the file. You can use the JsonNode class in the System.Text.Json.Nodes class. I'm using it to write back an encrypted connection string after reading a plain text one from appsettings.json.

There are examples of using Newtonsoft.Json.JsonConvert.DeserializeObject and deserializing into a dynamic type like @Alper suggested - but System.Text.Json could not do that. Well, now you sort of can :) (though not with a dynamic type).

In my example below, I tried to be minimalistic and simple. I used JsonNode to retrieve the value instead of a Dependency Injected IConfiguration. In a real web application, I'd be using the DI method. It really doesn't matter how you retrieve the setting, writing it back still means reconstructing the Json and updating the file on disk.

MS Link for JsonNode: https://learn.microsoft.com/en-us/dotnet/api/system.text.json.nodes.jsonnode?view=net-6.0

My appsettings.json sample:

{
  "sampleSection": {
    "someStringSetting": "Value One",
    "deeperValues": {
      "someIntSetting": 23,
      "someBooleanSetting": true
    }
  }
}

C# .NET 6 console application:

using System.Text.Json;
using System.Text.Json.Nodes;

const string AppSettingsPath = @"<PathToYourAppSettings.JsonFile>>\appsettings.json";
string appSettingsJson = File.ReadAllText(AppSettingsPath);
var jsonNodeOptions = new JsonNodeOptions { PropertyNameCaseInsensitive = true };
var node = JsonNode.Parse(appSettingsJson, jsonNodeOptions);

var options = new JsonSerializerOptions { WriteIndented = true };
Console.WriteLine("===========  Before ============");
Console.WriteLine(node.ToJsonString(options));


// Now you have access to all the structure using node["blah"] syntax
var stringSetting = (string) node["sampleSection"]["someStringSetting"];
var intSetting = (int) node["sampleSection"]["deeperValues"]["someIntSetting"];
var booleanSetting = (bool) node["sampleSection"]["deeperValues"]["someBooleanSetting"];

Console.WriteLine($"stringSetting: {stringSetting}, intSetting: {intSetting}, booleanSetting: {booleanSetting}");

// Now write new values back 
node["sampleSection"]["someStringSetting"] = $"New setting at {DateTimeOffset.Now}";
node["sampleSection"]["deeperValues"]["someIntSetting"] = -6;
node["sampleSection"]["deeperValues"]["someBooleanSetting"] = false;

Console.WriteLine("===========  After ============");
Console.WriteLine(node.ToJsonString(options));

// Or, to actually write it to disk: 
// File.WriteAllText(AppSettingsPath, node.ToJsonString(options));
Abbreviate answered 13/2, 2022 at 18:6 Comment(0)
S
2

Update value through this code it's simply run console application that reads application settings, adds a new setting, and updates an existing setting. and after update refresh the application on server without closed application.

For more information: See Microsoft .Net Docs, ConfigurationManager.AppSettings Property

static void AddUpdateAppSettings(string key, string value)
    {
        try
        {
            var configFile = System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration("~");
            var settings = configFile.AppSettings.Settings;
            if (settings[key] == null)
            {
                settings.Add(key, value);
            }
            else
            {
                settings[key].Value = value;
            }
            configFile.Save(ConfigurationSaveMode.Modified);
            ConfigurationManager.RefreshSection(configFile.AppSettings.SectionInformation.Name);
        }
        catch (ConfigurationErrorsException ex)
        {
            Console.WriteLine("Error writing app settings. Error: "+ ex.Message);
        }
    }
Shirring answered 27/1, 2022 at 10:49 Comment(2)
For me this created a new config file projectname.dll.config instead of using appsettings.json.Unconstitutional
Isn't this answer related to xml files??Boger
L
0

I hope that my scenario covers your intent, I wanted to override the appsettings.json values if there are environment variables passed to the app at startup.

I made use of the ConfigureOptions method that is available in dotnet core 2.1.

Here is the Model that is used for the JSON from appsettings.json

public class Integration
{
 public string FOO_API {get;set;}
}

For the services in the statup.cs:

var section = Configuration.GetSection ("integration");
            services.Configure<Integration> (section);
            services.ConfigureOptions<ConfigureIntegrationSettings>();

Here is the implemenation:

public class ConfigureIntegrationSettings : IConfigureOptions<Integration>
    {
        public void Configure(Integration options)
        {
            if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("FOO")))
                options.FOO_API = Environment.GetEnvironmentVariable("FOO_API");

        }
    }

so if there is no value set it falls back to the appsettings.json

Lovash answered 14/9, 2018 at 8:11 Comment(1)
This answer does not update the appsettings.json file.Robillard
E
0

I solved similar problem - I needed override appSettings like this:

For 'IConfigurationBuilder':

configurationBuilder
            .AddJsonFile("appsettings.json", false, true)
            .AddJsonFile($"appsettings.{environmentName}.json", false, true)
            .AddConfigurationObject(TenantsTimeZoneConfigurationOverrides(configurationBuilder)); // Override Tenants TimeZone configuration due the OS platform (https://dejanstojanovic.net/aspnet/2018/july/differences-in-time-zones-in-net-core-on-windows-and-linux-host-os/)

 private static Dictionary<string, string> TenantsTimeZoneConfigurationOverrides(IConfigurationBuilder configurationBuilder)
    {
        var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); 
        var overridesDictionary = new Dictionary<string, string>();
        var configuration = configurationBuilder.Build() as IConfiguration;
        var tenantsSection = configuration.GetSection(TenantsConfig.TenantsCollectionConfigSectionName).Get<Tenants>();
        foreach (var tenant in tenantsSection)
        {
            if (!string.IsNullOrEmpty(tenant.Value.TimeZone))
            {
                overridesDictionary.Add($"Tenants:{tenant.Key}:TimeZone", GetSpecificTimeZoneDueOsPlatform(isWindows, tenant.Value.TimeZone));
            }
        }
        return overridesDictionary;
    }

    private static string GetSpecificTimeZoneDueOsPlatform(bool isWindows, string timeZone)
    {
        return isWindows ? timeZone : TZConvert.WindowsToIana(timeZone);
    }
Eradicate answered 21/4, 2021 at 14:16 Comment(0)
R
0

Here is a solution using JPath.

This example allows for comments to be in the Json file as well.

Usage..

string key = "ConnectionStrings.BethanysPieShopDbContextConnection";
string Connectionstring = "Server=(localdb)\\mssqllocaldb;Database=BethanysPieShop8854;Trusted_Connection=True;MultipleActiveResultSets=true";
string _locOfAppSettingsFile = @"C:\Temp\appsettings.json";

UpdateAppSettingViaJPath(key, Connectionstring, _locOfAppSettingsFile);

Appsettings.json file...

   {
     //Connection string value
     "ConnectionStrings": {
         "BethanysPieShopDbContextConnection": "SampleString"
  },
    "Logging": {
     "LogLevel": {
     "Default": "Information",
     "Microsoft.AspNetCore": "Warning"
    }
 },
  "AllowedHosts": "*"
 }

Function

    public void UpdateAppSettingViaJPath(string key, string value, string settingsFileNameAndLocation = "appsettings.json")
    {
        // Empty keys "" are allowed in JSON, but should not be accepted here
        if (string.IsNullOrEmpty(key))
        {
            throw new ArgumentException("JSON property key cannot be null", nameof(key));
        }

        // Define the file path for the settings file
        string settingsFileName = settingsFileNameAndLocation;

        // Check if the settings file exists
        if (!File.Exists(settingsFileName))
        {
            throw new ArgumentException("Could not find Appsettings.Json", nameof(key));
        }

        // Read the contents of the settings file
        var config = File.ReadAllText(settingsFileName);

        // Parse the JSON content into a JObject
        var JObjAppSettingJson = JObject.Parse(config);

        // Select the token by the provided key and replace its value
        JObjAppSettingJson.SelectToken(key).Replace(value);

        // Serialize the modified JObject to JSON with pretty-print formatting
        string jsonString = JObjAppSettingJson.ToString(Formatting.Indented);

        // Write the updated JSON back to the settings file
        File.WriteAllText(settingsFileName, jsonString);
    }
Robillard answered 22/10, 2023 at 22:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.