将展平的分层数据转换为树形结构的JSON

时间:2013-11-12 12:50:39

标签: c# json linq tree

我有一个C#字典对象,其中包含该文件可用的文件和文件夹的名称。我想将数据转换为分层树。以下是数据。如何将其转换为树形结构的JSON。

我调查了这个example,但我无法获得所需的输出。

+-----------------------------------------------+|
|  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                  | 
+-----------------------------------------------+|

应生成如下输出:

   {"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
    }
]}

1 个答案:

答案 0 :(得分:5)

与您链接的示例一样,有两个主要任务。首先,我们需要将字典中的数据转换为分层形式。有一次,我们已经完成了,我们可以担心将其序列化为JSON。

首先,我们需要一个Node类来表示层次结构:

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

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

一旦我们有了,我们就可以浏览字典并构建树。 (注意:在您想要的JSON中,您将Paint bedroomOther显示为从属Remodeling,而在您的示例字典数据中,它们从属于Shopping。我假设在这种情况下JSON是正确的,所以我相应地更改了字典数据,如下所示。)

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 });
}

现在我们有了树,我们可以序列化它。但是,我们需要一些特殊的处理,因为您的叶节点的呈现方式与JSON中的非叶节点不同:叶节点具有leaf属性且没有children属性,而反之则为非叶节点。要处理此逻辑,我们需要自定义JsonConverter。 (只是为了澄清,我在这里使用Json.Net - 你的问题没有提到特定的JSON序列化器。)

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();
    }
}

我们可以使用JsonConverter将树序列化为JSON,如下所示:

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

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

Console.WriteLine(json);

这是输出:

{
  "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
    }
  ]
}

另一个小注:在上面所需的JSON中,您显示的根节点具有text属性而不是name属性,这与所有其他节点不一致。我假设这是一个错误。如果不是,您将需要更改JsonConverter,以便它具有输出text属性的逻辑,以代替name,如果名称是点(.

希望这有帮助。