'How to transform or convert dot (.) appended flat json structure to tree view (regular) json

I have below Json, I need to transform this dotted flat hierarchy into tree view hierarchy

    {  
      "info.clinical.user.gender.1": "female",
      "info.clinical.user.gender.2": "male",
      "info.clinical.user.age.max": "60",
      "info.clinical.user.age.min": "18",
    }

How to convert above Json to below JSON in a best and optimized way using C#

{
  "info": {
    "clinical": {
      "user": {
        "age": {
          "min": 18,
          "max": 60
        },
        "gender": [
          "female",
          "male"
        ]
      }
    }
  }
}


Solution 1:[1]

try this

   var origJsonObj = JObject.Parse(json);

    var arrFirst = origJsonObj.Properties().First().Name.Split('.');
    var jsonObj = new JObject();
    var prevObj = jsonObj;
    for (int i = 0; i < arrFirst.Length - 2; i++)
    {
        var currObj = new JObject();
        var newProperty = new JProperty(arrFirst[i], currObj);
        prevObj.Add(newProperty);
        prevObj = currObj;
    }

    foreach (var prop in origJsonObj.Properties())
    {
        var arr = prop.Name.Split('.');
        var name = arr[arr.Length - 2];
        if (int.TryParse(arr[arr.Length - 1], out var index))
        {
            JArray jArr;
            if (prevObj[name] == null)
            {
                jArr = new JArray();
                var newProperty = new JProperty(arr[arr.Length - 2], jArr);
                prevObj.Add(newProperty);
            }
            else jArr = (JArray)prevObj[name];

            jArr.Add(prop.Value);
            continue;
        }

        JObject jObj = null;
        if (prevObj[name] == null)
        {
            jObj = new JObject();
            prevObj.Add(new JProperty(name, jObj));
        }
        else jObj = (JObject)prevObj[name];

        jObj[arr[arr.Length - 1]] = prop.Value;
    }

Solution 2:[2]

Maybe you should implement it yourself, like:

using System.Text.Json.Nodes;

namespace FlatJson
{
    public static class FlatJson
    {
        public static JsonObject ToTree(this JsonObject flat, char sep = '.')
        {
            var result = new JsonObject();
            foreach (var flatItem in flat)
            {
                var layerNames = flatItem.Key.Split(sep);
                var lastIndex = layerNames.Length - 1;

                var currentLayer = result;
                for (int layerIndex = 0; layerIndex < lastIndex; layerIndex++)
                {
                    var layerName = layerNames[layerIndex];
                    var nextLayer = currentLayer[layerName]?.AsObject();
                    if (nextLayer is not null)
                        currentLayer = nextLayer;
                    else
                    {
                        nextLayer = new JsonObject();
                        currentLayer[layerName] = nextLayer;
                        currentLayer = nextLayer;
                    }
                }

                var propertyName = layerNames[lastIndex];
                var value = flatItem.Value?.GetValue<object?>();
                currentLayer[propertyName] = JsonValue.Create(value);
            }
            return result;
        }
    }
}

And how to use it:

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

using FlatJson;

var t = File.ReadAllText("test.json");

JsonObject? obj = JsonNode.Parse(t)?.AsObject();
Debug.Assert(obj is not null);

JsonObject result = obj.ToTree();

Console.WriteLine(obj);
Console.WriteLine();
Console.WriteLine(result);

But as I asked in the comments, we don't really know whether the "60" here is a number or not, and whether the info.clinical.user.gender is a list or an object. So the output of my code is (which makes sense to me):

(The original json has been changed to show you how it works in some other situations.)

{
  "info.clinical.user.gender.1": "60",
  "info.clinical.user.gender.2": "male",
  "info.clinical.user.age.max": 60,
  "info.clinical.user.age.min": null
}

{
  "info": {
    "clinical": {
      "user": {
        "gender": {
          "1": "60",
          "2": "male"
        },
        "age": {
          "max": 60,
          "min": null
        }
      }
    }
  }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Serge
Solution 2 yueyinqiu