4

I have a C# dictionary object which contains name of file and folder under which that file is available. I want to convert the data into hierarchical tree. Below is the data. How can I convert it into tree structured JSON.

i looked into this example but I am not able to get the desired output.

+-----------------------------------------------+|
|  Name             | Path 
|------------------------------------------------|
| Kitchen supplies  |  Shopping / Housewares     |
| Groceries         |  Shopping / Housewares     |
| Cleaning supplies |  Shopping / Housewares     |
| Office supplies   |  Shopping / Housewares     |
| Retile kitchen    |  Shopping / Remodeling     |
| Ceiling           |  Shopping / Paint bedroom  |
| Walls             |  Shopping / Paint bedroom  |
| Misc              |  null                      |
| Other             |  Shopping                  | 
+-----------------------------------------------+|

Should Generate output like:

   {"text":".","children": [
    {
        Name:' Shopping',
        children:[{
            Name:'Housewares',
            children:[{
                Name:'Kitchen supplies',
                leaf:true,
            },{
                Name:'Groceries',
                leaf:true,
            },{
                Name:'Cleaning supplies',
                leaf:true,
            },{
                Name: 'Office supplies',
                leaf: true,
            }]
        }, {
            Name:'Remodeling',
            children:[{
                Name:'Retile kitchen',
                leaf:true,
            },{
                Name:'Paint bedroom',
                children: [{
                    Name: 'Ceiling',
                    leaf: true
                }, {
                    Name: 'Walls',
                    iconCls: 'Name',
                }]
            },
            {
                Name: 'Other',
                leaf: true
            }]
        }]
    },
    {
        Name: 'Misc',
        leaf: true
    }
]}
Community
  • 1
  • 1
SharpCoder
  • 18,279
  • 43
  • 153
  • 249

1 Answers1

6

As in the example you linked to, there are two main tasks. First, we need to get the data from the dictionary into a hierarchical form. Once, we've done that, we can worry about serializing it to JSON.

So first thing, we need a Node class to represent the hierarchy:

class Node
{
    public Node()
    {
        Children = new List<Node>();
    }

    public string Name { get; set; }
    public List<Node> Children { get; set; }
}

Once we have that, we can go through the dictionary and build the tree. (Note: in your desired JSON, you show Paint bedroom and Other as subordinate to Remodeling, while in your example dictionary data they are subordinate to Shopping. I am assuming the JSON is correct in this case, so I changed the dictionary data accordingly as shown below.)

Dictionary<string, string> dict = new Dictionary<string, string>();
dict.Add("Kitchen supplies", "Shopping / Housewares");
dict.Add("Groceries", "Shopping / Housewares");
dict.Add("Cleaning supplies", "Shopping / Housewares");
dict.Add("Office supplies", "Shopping / Housewares");
dict.Add("Retile kitchen", "Shopping / Remodeling");
dict.Add("Ceiling", "Shopping / Remodeling / Paint bedroom");
dict.Add("Walls", "Shopping / Remodeling / Paint bedroom");
dict.Add("Misc", null);
dict.Add("Other", "Shopping / Remodeling");

Node root = new Node();
foreach (KeyValuePair<string, string> kvp in dict)
{
    Node parent = root;
    if (!string.IsNullOrEmpty(kvp.Value))
    {
        Node child = null;
        foreach (string part in kvp.Value.Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries))
        {
            string name = part.Trim();
            child = parent.Children.Find(n => n.Name == name);
            if (child == null)
            {
                child = new Node { Name = name };
                parent.Children.Add(child);
            }
            parent = child;
        }
    }
    parent.Children.Add(new Node { Name = kvp.Key });
}

Now that we have our tree, we can serialize it. However, we need some special handling because your leaf nodes are rendered differently than non-leaf nodes in your JSON: leaf nodes have a leaf property and no children property, while the reverse is true for non-leaf nodes. To handle this logic, we will need a custom JsonConverter. (Just to clarify, I am using Json.Net here-- your question did not mention a specific JSON serializer.)

class NodeConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(Node));
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        Node node = (Node)value;
        JObject jo = new JObject();
        jo.Add("name", node.Name);
        if (node.Children.Count == 0)
        {
            jo.Add("leaf", true);
        }
        else
        {
            jo.Add("children", JArray.FromObject(node.Children, serializer));
        }
        jo.WriteTo(writer);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

We can use the JsonConverter to serialize the tree to JSON like this:

JsonSerializerSettings settings = new JsonSerializerSettings
{
    Converters = new List<JsonConverter> { new NodeConverter() },
    Formatting = Formatting.Indented
};

string json = JsonConvert.SerializeObject(root, settings);

Console.WriteLine(json);

Here is the output:

{
  "name": ".",
  "children": [
    {
      "name": "Shopping",
      "children": [
        {
          "name": "Housewares",
          "children": [
            {
              "name": "Kitchen supplies",
              "leaf": true
            },
            {
              "name": "Groceries",
              "leaf": true
            },
            {
              "name": "Cleaning supplies",
              "leaf": true
            },
            {
              "name": "Office supplies",
              "leaf": true
            }
          ]
        },
        {
          "name": "Remodeling",
          "children": [
            {
              "name": "Retile kitchen",
              "leaf": true
            },
            {
              "name": "Paint bedroom",
              "children": [
                {
                  "name": "Ceiling",
                  "leaf": true
                },
                {
                  "name": "Walls",
                  "leaf": true
                }
              ]
            },
            {
              "name": "Other",
              "leaf": true
            }
          ]
        }
      ]
    },
    {
      "name": "Misc",
      "leaf": true
    }
  ]
}

One other minor note: in your desired JSON above, you show the root node with a text property instead of a name property, which is inconsistent with all the other nodes. I am assuming this was a mistake. If it wasn't, you'll need to change the JsonConverter so that it has logic to output a text property in place of the name if the name is a dot (.).

Hope this helps.

Brian Rogers
  • 125,747
  • 31
  • 299
  • 300