如何使用 JSON 数据递归填充 TreeView

Posted

技术标签:

【中文标题】如何使用 JSON 数据递归填充 TreeView【英文标题】:How to recursively populate a TreeView with JSON data 【发布时间】:2017-02-02 02:01:04 【问题描述】:

我有一个winforms treeview,我可以自动读取数据,(一个节点等于key,一个节点里面等于value),但是读取对象类型时,里面的值不会是对象节点的子节点(对象的键),(可能我解释不好,这里是截图和我的方法。)

layer0 需要在纹理内部,比例需要在显示内部

我的 Json:


"parent": "builtin/generated",
"textures": 
    "layer0": "mm:items/iron_dust"
,
"display":        
        "scale": [ 1.7, 1.7, 1.7 ]
 

我的自动检测方法(实际上并非都是我的)

private void Form1_Load(object sender, EventArgs e)
    
        StreamReader reader = new StreamReader(path);
        string json = reader.ReadToEnd();
        reader.Close();
        JObject obj = JObject.Parse(json);
        getAllProperties(obj);
    

    void getAllProperties(JToken children)
    
        TreeNode mainNode = treeView1.Nodes[0];
        mainNode.Text = Path.GetFileNameWithoutExtension(path);
        foreach (JToken child in children.Children())
        
            var property = child as JProperty;
            if (property != null)
            
                if (property.Value.Type == JTokenType.String)
                
                    TreeNode keyNode = mainNode.Nodes.Add(property.Name);
                    keyNode.Nodes.Add(property.Value.ToString());
                
                if (property.Value.Type == JTokenType.Array)
                
                    JArray array = (JArray)property.Value;
                    TreeNode node = mainNode.Nodes.Add(property.Name);
                    for (int i = 0; i < array.Count; i++)
                    
                        node.Nodes.Add(array[i].ToString());
                    
                
                if (property.Value.Type == JTokenType.Object)
                
                    TreeNode topNode = mainNode.Nodes.Add(property.Name.ToString());
                    foreach (var item in property)
                    
                        if (item.Type == JTokenType.String)
                        
                             if (property.Value.Type == JTokenType.String)
                
                    TreeNode keyNode = topNode.Nodes.Add(property.Name);
                    keyNode.Nodes.Add(property.Value.ToString());
                
                if (property.Value.Type == JTokenType.Array)
                
                    JArray array = (JArray)property.Value;
                    TreeNode node = topNode.Nodes.Add(property.Name);
                    for (int i = 0; i < array.Count; i++)
                    
                        node.Nodes.Add(array[i].ToString());
                    
                
                        
                    
                


                    // Console.WriteLine(property.Name + ":" + property.Value);//print all of the values
                
                getAllProperties(child);
            
        

    

我尝试获取父级,但它没有名称和值属性:S。 有什么帮助吗? (语言错误见谅)

【问题讨论】:

也许this thread 能帮上忙? 我会尽可能尝试:)(我现在不能尝试)但是..我不明白 walknode 在这里做什么。你能解释一下吗? WalkNode 的工作方式如下:从参数中获取节点,然后遍历节点的每个子节点。它应用 Action 函数(在您的情况下可以添加到 MainNode 中),然后更深入 - 在子节点上调用 WalkNode。基本上,它递归遍历 JSON 中的所有节点。 你可以复制你在foreach循环中的逻辑(你在这里访问你的TreeNode的mainNode,对吧?)并将它插入Action函数中,在WalkNode中引用 可能是的,但我不能确定,因为我没有尝试使用你的代码,也没有自己用这种方式解析 JSON。 【参考方案1】:

问题是,当您递归下降JToken 层次结构时,您还需要递归下降您正在创建的TreeNode 层次结构,将子节点添加到刚刚创建的父节点,而不是根节点,沿Recursion, parsing xml file with attributes into treeview c# 的行。

如果你这样做:

    private void Form1_Load(object sender, EventArgs e)
    
        using (var reader = new StreamReader(path))
        using (var jsonReader = new JsonTextReader(reader))
        
            var root = JToken.Load(jsonReader);
            DisplayTreeView(root, Path.GetFileNameWithoutExtension(path));
        
    

    private void DisplayTreeView(JToken root, string rootName)
    
        treeView1.BeginUpdate();
        try
        
            treeView1.Nodes.Clear();
            var tNode = treeView1.Nodes[treeView1.Nodes.Add(new TreeNode(rootName))];
            tNode.Tag = root;

            AddNode(root, tNode);

            treeView1.ExpandAll();
        
        finally
        
            treeView1.EndUpdate();
        
    

    private void AddNode(JToken token, TreeNode inTreeNode)
    
        if (token == null)
            return;
        if (token is JValue)
        
            var childNode = inTreeNode.Nodes[inTreeNode.Nodes.Add(new TreeNode(token.ToString()))];
            childNode.Tag = token;
        
        else if (token is JObject)
        
            var obj = (JObject)token;
            foreach (var property in obj.Properties())
            
                var childNode = inTreeNode.Nodes[inTreeNode.Nodes.Add(new TreeNode(property.Name))];
                childNode.Tag = property;
                AddNode(property.Value, childNode);
            
        
        else if (token is JArray)
        
            var array = (JArray)token;
            for (int i = 0; i < array.Count; i++)
            
                var childNode = inTreeNode.Nodes[inTreeNode.Nodes.Add(new TreeNode(i.ToString()))];
                childNode.Tag = array[i];
                AddNode(array[i], childNode);
            
        
        else
        
            Debug.WriteLine(string.Format("0 not implemented", token.Type)); // JConstructor, JRaw
        
    

您将获得以下树状视图结构:

【讨论】:

哇!这就是我所需要的。但我想知道一些事情。这是否适用于布尔值: if (token is JBoolean) var childNode = inTreeNode.Nodes[inTreeNode.Nodes.Add(new TreeNode(token.ToString()))]; childNode.Tag = 令牌; 没有JBoolean 这样的东西。布尔值存储在JValue。所有具体的 linq-to-JSON 类型的集合显示在 this answer 中。【参考方案2】:

这是我的破解之道。输出与 Notepad++ 的 JSTool 插件相同:

代码结构为 TreeView 扩展:

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using System.Windows.Forms;

namespace TestDLApp.Utilities.Extensions

    public static class ObjectToTreeView
    
        private sealed class IndexContainer
        
            private int _n;
            public int Inc() => _n++;
        

        private static void FillTreeView(TreeNode node, JToken tok, Stack<IndexContainer> s)
        
            if (tok.Type == JTokenType.Object)
            
                TreeNode n = node;
                if(tok.Parent != null)
                
                    if(tok.Parent.Type == JTokenType.Property)
                    
                        n = node.Nodes.Add($"((JProperty)tok.Parent).Name <tok.Type.ToString()>");
                    
                    else
                    
                        n = node.Nodes.Add($"[s.Peek().Inc()] <tok.Type.ToString()>");
                    
                
                s.Push(new IndexContainer());
                foreach (var p in tok.Children<JProperty>())
                
                    FillTreeView(n, p.Value, s);
                
                s.Pop();
            
            else if (tok.Type == JTokenType.Array)
            
                TreeNode n = node;
                if(tok.Parent != null)
                
                    if (tok.Parent.Type == JTokenType.Property)
                    
                        n = node.Nodes.Add($"((JProperty)tok.Parent).Name <tok.Type.ToString()>");
                    
                    else
                    
                        n = node.Nodes.Add($"[s.Peek().Inc()] <tok.Type.ToString()>");
                    
                
                s.Push(new IndexContainer());
                foreach (var p in tok)
                
                    FillTreeView(n, p, s);
                
                s.Pop();
            
            else
            
                var name = string.Empty;
                var value = JsonConvert.SerializeObject(((JValue)tok).Value);

                if (tok.Parent.Type == JTokenType.Property)
                
                    name = $"((JProperty)tok.Parent).Name : value";
                
                else
                
                    name = $"[s.Peek().Inc()] : value";
                

                node.Nodes.Add(name);
            
        

        public static void SetObjectAsJson<T>(this TreeView tv, T obj)
        
            tv.BeginUpdate();
            try
            
                tv.Nodes.Clear();

                var s = new Stack<IndexContainer>();
                s.Push(new IndexContainer());
                FillTreeView(tv.Nodes.Add("ROOT"), JsonConvert.DeserializeObject<JToken>(JsonConvert.SerializeObject(obj)), s);
                s.Pop();
            
            finally
            
                tv.EndUpdate();
            
        
    

你可以这样称呼它:

treeView1.SetObjectAsJson(new MyNeatObject());

【讨论】:

以上是关于如何使用 JSON 数据递归填充 TreeView的主要内容,如果未能解决你的问题,请参考以下文章

如何在剃须刀页面中实现 Treeview

从数据库填充 TreeView

treeview 无限极菜单

TreeView、HierarchicalDataTemplate 和递归数据

C#如何从json文件动态地制作treeview

如何在 TreeView 中禁用或修改 TreeCell 的填充