0

I have the below dictionary, and I need to convert the complex key values into proper JSON.

static void Main(string[] args)
        {
            Dictionary<string, object> collectionProp = new Dictionary<string, object>();
            Dictionary<string, object> prop = new Dictionary<string, object>();
            prop.Add("content", "button");
            prop.Add("page.count", "10");
            prop.Add("columns.0.textAlign", "center");
            prop.Add("columns.1.textAlign", "left");
            var result = new Dictionary<string, object>();
            foreach (var pair in prop)
            {
                var key = pair.Key;                
                    var parts = key.Split('.');
                    var currentObj = result;
                    for (int i = 0; i < parts.Length - 1; i++)
                    {
                        var property = parts[i];
                        if (!currentObj.Keys.Contains(property))
                            currentObj[property] = new Dictionary<string, object>();
                        currentObj = (Dictionary<string, object>)currentObj[property];
                    }
                    currentObj[parts[parts.Length - 1]] = pair.Value;                
            }
            Console.WriteLine(JsonConvert.SerializeObject(result, Formatting.Indented));
            Console.ReadLine();
        }

And getting the following result.

{
  "content": "button",
  "page": {
    "count": "10"
  },
  "columns": {
    "0": {
      "textAlign": "center"
    },
    "1": {
      "textAlign": "left"
    }
  }
}

Expecting the columns should be grouped as JSON array. How to achieve this.?

The desired output:

{
  "content": "button",
  "page": {
    "count": "10"
  },
  "columns": [
    {
      "textAlign": "center"
    },
    {
      "textAlign": "left"
    }
  ]
}
Karthick
  • 1,241
  • 5
  • 20
  • 43
  • Please edit your question to show an example of your desired output – stuartd Jun 06 '19 at 11:17
  • 1
    Why do you expect `columns` to be an array? You have no special handling for it, you're just creating nested `Dictionary` objects. And as far as JSON.NET is concerned, `"0"` and `"1"` are not special either. – user247702 Jun 06 '19 at 11:17
  • @stuartd updated the desired output – Karthick Jun 06 '19 at 11:19
  • @Stijn : Yes, that won't be a concern with JSON.NET, the desired output will be passed to underlying control properties to update the control UI. – Karthick Jun 06 '19 at 11:21
  • 1
    @Karthick, Why you choose dictionary to build json? – er-sho Jun 06 '19 at 11:37
  • @er-sho : Dynamically pushing the values into proper keys, then convert it into JSON to update the control properties. Keys plays a role to hold and updating the control properties. – Karthick Jun 06 '19 at 11:42
  • It seems that using just typed Objects would be far simpler and cleaner than using a Dictionary. – Steve Jun 06 '19 at 12:38
  • 1
    See here for an easier way : https://dotnetfiddle.net/teS6cV – Steve Jun 06 '19 at 12:52
  • @Steve: Thanks for your effort, and I want to let you know, the keys & values are dynamic types and values. In another scenario, the keys may differ, instead of column, we tend to parse the columnSettings which has inner level properties. – Karthick Jun 06 '19 at 17:12

1 Answers1

1

JSON.NET will serialize dictionaries as JSON objects by default even though its keys are convertible to an integer. Instead of building a dictionary from the source you could build JObject hierarchy. These helper methods recognize array indices indices in your path to automatically build JArray container around them:

public static class JsonExtensions
{
    public static void SetValue(this JContainer container, string path, object value)
    {
        JToken token = container;
        var keys = path.Split('.');
        foreach (var key in keys)
        {
            int index;
            if (int.TryParse(key, out index))
            {
                var jArray = token as JArray;
                if (jArray == null)
                {
                    jArray = new JArray();
                    token.Replace(jArray);
                    token = jArray;
                }
                while (index >= jArray.Count)
                {
                    jArray.Add(JValue.CreateNull());
                }
                token = jArray[index];
            }
            else
            {
                var jObject = token as JObject;
                if (jObject == null)
                {
                    jObject = new JObject();
                    token.Replace(jObject);
                    token = jObject;
                }
                token = token[key] ?? (token[key] = JValue.CreateNull());
            }
        }
        token.Replace(new JValue(value));
    }

    public static void SetValues(this JContainer container, IEnumerable<KeyValuePair<string, object>> pairs)
    {
        foreach (var pair in pairs)
        {
            container.SetValue(pair.Key, pair.Value);
        }
    }
}

And here's how you get the results you expect:

var jObject = new JObject();
jObject.SetValues(new Dictionary<string, object>
{
    { "content", "button" },
    { "page.count", "10" },
    { "columns.0.textAlign", "center" },
    { "columns.1.textAlign", "left" }
});
Console.WriteLine(jObject.ToString(Formatting.Indented));

Note that the code sample I provided is no way near efficient and should be used just as an inspiration to achieve the result you require.

Also note that in some cases the order of values to build the JObject matters, but enumerating items from dictionary is non-deterministic. Therefore you might consider a better data structure for the source that guarantees order of key-value pairs in it such as an array.

Peter Wolf
  • 3,700
  • 1
  • 15
  • 30
  • @dbc Yes, and that's perfectly fine. I was just pointing out that when a **dictionary** is used as **the source** enumerable of KVP to build `JObject` the result will be undefined. Consider dictionary containing `{{"key", "value"}, {"key.section", "value"}}` may result either in `{"key": "value"}` or `{"key": { "section": "value"}}` – Peter Wolf Jun 07 '19 at 18:34