1

We have a process during a MSI based installation were we are taking in appsetting values via paramaters and/or an installer form.

I am trying to dynamically update the json properties of the applications appsettings file but am not sure how to search the json using the key format of "node:child:child" at any depth. All options are on the table, we are able to serialize the object and use reflection, a linq solution or anything else. This will be very similar to how something like the json replacement works in an Azure build or release pipeline.

Currently have this which is very close except I cannt extract the json string from the IConfiguration in order to write it back to the file.....

public static ActionResult ModifySettings(Session session)
        {
            session.Log("Begin ModifySettings");

            try
            {
                string[] customData = session.CustomActionData.Keys.ToArray();
                string[] customValues = session.CustomActionData.Values.ToArray();

                ///Product.wxs -> BinDir=[BinDir]
                var installDirectory = customValues[1];
                string file = Path.Combine(installDirectory, "appsettings.json");

                session.Log(string.Format($"Updating file: {file}.{Environment.NewLine}."));

                if (File.Exists(file))
                {
                    session.Log($"Updating Settings file: {file}.{Environment.NewLine}");

                    var configFile = new ConfigurationBuilder().AddJsonFile(file).Build();                    

                    for (int i = 3; i < customData.Count(); i++)
                    {
                        if (!ReplaceSetting(configFile, customData[i], customValues[i], session))
                            throw new ArgumentException($"Error during settings replacement of {customData[i]} with {customValues[i]}",customData[i]);
                    }

                    var settingsAsString = configFile.ToString();//DOES NOT WORK
                    session.Log($"Updated settings{Environment.NewLine}{settingsAsString}{Environment.NewLine}");
                    File.WriteAllText(file, settingsAsString);
                }
                else
                {
                    session.Log($"Failed to update file {file}.{Environment.NewLine}Exiting.");
                    return ActionResult.Failure;
                }

                return ActionResult.Success;
            }catch(Exception ex)
            {
                session.Log(ex.Message);
                return ActionResult.Failure;
            }
        }
 private static bool ReplaceSetting(IConfigurationRoot settings, string settingName, string settingValue, Session session)
        {
            try
            {
                session.Log(string.Format($"Updating Setting: {settingName}{Environment.NewLine}Value: {settingValue}{Environment.NewLine}"));
                settings[settingName] = settingValue; //<- THIS IS THE PART I NEED TO MIMIC WHEN SETTINGNAME FORMAT IS SOMETHING:SOMETHINGELSE:PROPERTY!!!
            }
            catch(Exception ex)
            {
                session.Log(ex.Message);
                return false;
            }
            return true;
        }

EDIT Working solution using JObject

 public static ActionResult ModifySettings(Session session)
        {
            session.Log("Begin ModifySettings");

            try
            {
                string[] customData = session.CustomActionData.Keys.ToArray();
                string[] customValues = session.CustomActionData.Values.ToArray();

                ///Product.wxs -> BinDir=[BinDir]
                var installDirectory = customValues[1];
                string file = Path.Combine(installDirectory, "appsettings.json");

                session.Log(string.Format($"Updating file: {file}.{Environment.NewLine}."));

                if (File.Exists(file))
                {
                    session.Log($"Updating Settings file: {file}.{Environment.NewLine}");

                    var configFile = new ConfigurationBuilder().AddJsonFile(file).Build();
                    JObject settings = JObject.Parse(File.ReadAllText(file));

                    for (int i = 3; i < customData.Count(); i++)
                    {
                        if (!ReplaceSetting(ref settings, customData[i], customValues[i], session))
                            throw new ArgumentException($"Error during settings replacement of {customData[i]} with {customValues[i]}",customData[i]);
                    }

                    session.Log($"Updated settings{Environment.NewLine}{settings.ToString()}{Environment.NewLine}");
                    File.WriteAllText(file, settings.ToString());
                }
                else
                {
                    session.Log($"Failed to update file {file}.{Environment.NewLine}Exiting.");
                    return ActionResult.Failure;
                }

                return ActionResult.Success;
            }catch(Exception ex)
            {
                session.Log(ex.Message);
                return ActionResult.Failure;
            }
        }

private static bool ReplaceSetting(ref JObject settingFile, string settingName, string settingValue, Session session)
        {
            try
            {
                session.Log(string.Format($"Updating Setting: {settingName}{Environment.NewLine}Value: {settingValue}{Environment.NewLine}"));
                var token = settingFile.SelectToken(settingName);
                (token.Parent as JProperty).Value = settingValue;
            }
            catch (Exception ex)
            {
                session.Log(ex.Message);
                return false;
            }
            return true;
        }
Paul Swetz
  • 2,234
  • 1
  • 11
  • 28
  • The normal way to edit data stored in JSON format is to deserialise the JSON to an object, edit the properties of the object (just like any other C# variable), and then serialize the object back to JSON again. Using a library like Newtonsoft.Json you can deserialise either to a (set of) concrete type(s) which match the structure of the JSON, or to a generic type like JObject or JArray provided by Newtonsoft, or even to `dynamic` if you prefer. Have you actually tried or researched anything so far? – ADyson May 07 '19 at 15:04
  • Added context related to serialization – Paul Swetz May 07 '19 at 15:23
  • Well once you've deserialised it, then assuming you have an object called "data" which is of type `Settings` then you can access the value like this: `var value = data.Internal1.Internal2.Someprop` . Or are you asking how to find a property whose name and location you don't know at compile-time? Or how to add a new property? Your exact requirement is a little unclear...but if you are trying to do either/both of those tasks then deserializing to `dynamic` or `JObject` would allow you to do that much more easily than working with concrete types which cannot be modified at runtime. – ADyson May 07 '19 at 15:29
  • Yes the settings file is not known at compile time so the property lookup has to be done dynamically. Not having a concrete settings type would be ideal. – Paul Swetz May 07 '19 at 15:32
  • 1
    Well, then, using `dynamic` or `JObject` would be more suitable then. Then you can iterate or search through the structure to find what you need. I'd expect you can probably find material relating to this (or something very similar which you can adapt) online already with a bit of searching. – ADyson May 07 '19 at 15:33
  • Is [this](https://stackoverflow.com/a/21695462/8534588) what you're looking for? – Josh Withee May 07 '19 at 15:39
  • @Marathon55 its close however the part using this syntax has to be dynamic, jsonObj["Bots"][0]["Password"] = "new password"; So something like jsonObj["Bots.0.Password"] = "value" would be the desired usage. In the end if that was possible it would actually end up being jsonObj[searchstring] = "value"; – Paul Swetz May 07 '19 at 15:41
  • @ADyson updated example with an almost complete version of what we are going for – Paul Swetz May 07 '19 at 17:17
  • Are you saying that's a version which works for you, or there's still a problem to be solved? – ADyson May 07 '19 at 17:33
  • Iconfiguration has no save/persistence and I cannot extract the json string back from it so its close but does not allow me save the settings in this example. – Paul Swetz May 07 '19 at 17:35

0 Answers0