Search code examples
c#jsonserializationjson.net

Serialize flat Dictionary into multi-sub-object JSON


In C# I have a flat Dictionary<string, string> where keys are in form of obj1/obj2/obj3 and values are direct string. Now I want to serialize that into subobjects, so example values:

var dict = new Dictionary<string, string> { {"foo/bar/baz1", "123" }, {"foo/baz", "456" }, { "foo/abc", "def" } };

should result with:

{
     "foo": {
         "bar": {
             "baz1": "123"
         },
         "baz": "456",
         "abc": "def"
     }
}

Optionally I want to remove quotes around "123" and "456" in output if they can be interpreted as numbers or booleans.

I am using Newtonsoft.JSON


Solution

  • You can parse a source dictionary into JObject using JObject.FromObject method. Then go through all of properties, split them using string.Split and parse recursively to a new JObject, representing a properties tree. Finally add this object to the destination one using JObject.Add, or update it if the given key is already exist

    var dict = new Dictionary<string, string> { { "foo/bar/baz1", "123" }, { "foo/baz", "456" }, { "foo/abc", "def" } };
    var source = JObject.FromObject(dict);
    var dest = new JObject();
    
    foreach (var property in source.Properties())
    {
        //split the name into parts
        var items = property.Name.Split(new[] { "/" }, StringSplitOptions.RemoveEmptyEntries);
        var item = items.FirstOrDefault();
        if (string.IsNullOrEmpty(item))
            continue;
    
        //get JObject representing a properties tree
        var result = WriteItems(items.Skip(1).ToList(), property.Value);
    
        //check that destination already contains top property name (e.g. 'foo')
        if (dest.ContainsKey(item))
        {
            (dest[item] as JObject)?.Add(result.First);
        }
        else
        {
            dest.Add(item, result);
        }
    }
    
    Console.WriteLine(dest.ToString());
    
    //local function to recursively go through all properties and create a result JObject
    JObject WriteItems(IList<string> items, JToken value)
    {
        var item = items.FirstOrDefault();
        items.RemoveAt(0);
    
        if (!items.Any()) //no more items in keys, add the value
            return new JObject(new JProperty(item, value));
    
        return new JObject(new JProperty(item, WriteItems(items, value)));
    }
    

    It produces the following output

    {
      "foo": {
        "bar": {
          "baz1": "123"
        },
        "baz": "456",
        "abc": "def"
      }
    }
    

    Also, the code above allows you to handle a properties tree with any depth. I don't think that there is a built-in way to serialize the structure like foo/bar/baz1 into sub-objects in Json.NET