Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How convert IConfigurationRoot or IConfigurationSection to JObject/JSON

I have the following code in my Program.cs:

var configuration = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("clientsettings.json", optional: true, reloadOnChange: true)
                .AddJsonFile($"clientsettings.{host.GetSetting("environment")}.json", optional: true, reloadOnChange: true)
                .AddEnvironmentVariables()
                .Build();

I want to convert the result of building my configuration to JObject\Json for sending to the client. How can I do it? and I don't want to create my custom class for my settings.

My answer: merge

public static JObject GetSettingsObject(string environmentName)
    {
        object[] fileNames = { "settings.json", $"settings.{environmentName}.json" };


        var jObjects = new List<object>();

        foreach (var fileName in fileNames)
        {
            var fPath = Directory.GetCurrentDirectory() + Path.DirectorySeparatorChar + fileName;
            if (!File.Exists(fPath))
                continue;

            using (var file = new StreamReader(fPath, Encoding.UTF8))
                jObjects.Add(JsonConvert.DeserializeObject(file.ReadToEnd()));
        }


        if (jObjects.Count == 0)
            throw new InvalidOperationException();


        var result = (JObject)jObjects[0];
        for (var i = 1; i < jObjects.Count; i++)
            result.Merge(jObjects[i], new JsonMergeSettings
            {
                MergeArrayHandling = MergeArrayHandling.Merge
            });

        return result;
    }
like image 925
heoLixfy Avatar asked Feb 03 '23 20:02

heoLixfy


1 Answers

Since configuration is actually just a key value store where the keys have a certain format to represent a path, serializing it back into a JSON is not that simple.

What you could do is recursively traverse through the configuration children and write its values to a JObject. This would look like this:

public JToken Serialize(IConfiguration config)
{
    JObject obj = new JObject();
    foreach (var child in config.GetChildren())
    {
        obj.Add(child.Key, Serialize(child));
    }

    if (!obj.HasValues && config is IConfigurationSection section)
        return new JValue(section.Value);

    return obj;
}

Note that this is extremely limited in how the output looks. For example, numbers or booleans, which are valid types in JSON, will be represented as strings. And since arrays are represented through numerical key paths (e.g. key:0 and key:1), you will get property names that are strings of indexes.

Let’s take for example the following JSON:

{
  "foo": "bar",
  "bar": {
    "a": "string",
    "b": 123,
    "c": true
  },
  "baz": [
    { "x": 1, "y": 2 },
    { "x": 3, "y": 4 }
  ]
}

This will be represented in configuration through the following key paths:

"foo"      -> "bar"
"bar:a"    -> "string"
"bar:b"    -> "123"
"bar:c"    -> "true"
"baz:0:x"  -> "1"
"baz:0:y"  -> "2"
"baz:1:x"  -> "3"
"baz:1:y"  -> "4"

As such, the resulting JSON for the above Serialize method would look like this:

{
  "foo": "bar",
  "bar": {
    "a": "string",
    "b": "123",
    "c": "true"
  },
  "baz": {
    "0": { "x": "1", "y": "2" },
    "1": { "x": "3", "y": "4" }
  }
}

So this will not allow you to get back the original representation. That being said, when reading the resulting JSON again with Microsoft.Extensions.Configuration.Json, then it will result in the same configuration object. So you can use this to store the configuration as JSON.

If you want anything prettier than that, you will have to add logic to detect array and non-string types, since both of these are not concepts of the configuration framework.


I want to merge appsettings.json and appsettings.{host.GetSetting("environment")}.json to one object [and send that to the client]

Keep in mind that environment-specific configuration files often contain secrets that shouldn’t leave the machine. This is also especially true for environment variables. If you want to transmit the configuration values, then make sure not to include the environment variables when building the configuration.

like image 108
poke Avatar answered Feb 06 '23 10:02

poke