13

I have a winforms treeview, I can read data automatically, (a node that is equal to key, and a node inside that is equal to value), but when reading object type, the values inside it are not going to be child of object node (key of object), (maybe I couldnt explain well, here is a screenshot and my methods.)

layer0 needs to be inside textures and scale needs to be inside display

layer0 needs to be inside textures and scale needs to be inside display

My Json:

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

My method to auto detect(not all mine actually)

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

    }

I tried to get parent, but it didnt have name and value properties :S. Any help? (Sorry for language mistakes)

9
  • 1
    Maybe this thread could help? Commented Sep 24, 2016 at 9:00
  • I will try when i can :) (I can't try it now) But.. i do not understand what walknode does here. Could you explain please? Commented Sep 24, 2016 at 9:14
  • 2
    WalkNode works like this: gets the node from argument, then iterates over every child of the node. It applies the Action function (which can be adding to the MainNode in your case) and then goes level deeper - calss WalkNode on the child. Basically, it recursively walks all the nodes in the JSON. Commented Sep 24, 2016 at 9:18
  • 1
    You could copy the logic you have in your foreach loop (you access the mainNode of your TreeNode here, right?) and insert it in the Action function, referenced in WalkNode Commented Sep 24, 2016 at 9:35
  • 1
    Probably yes, but I can't be sure as I haven't tried to use your code anyway nor had to parse JSON this way myself. Commented Sep 24, 2016 at 9:44

2 Answers 2

21

The problem is that, as you recursively descend the JToken hierarchy, you also need to recursively descend the TreeNode hierarchy you are creating, adding child nodes to the parent node just created, rather than the root node, along the lines of Recursion, parsing xml file with attributes into treeview c#.

Thus if you do:

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

You will get the following tree view structure:

enter image description here

Sign up to request clarification or add additional context in comments.

2 Comments

Whoa! This what all i need. But i wonder something. Does this work for booleans : if (token is JBoolean) {var childNode = inTreeNode.Nodes[inTreeNode.Nodes.Add(new TreeNode(token.ToString()))]; childNode.Tag = token;}
There's no such thing as JBoolean. Booleans are stored in JValue. The set of all concrete linq-to-JSON types is shown in this answer.
11

Here is my crack at it. The output is identical to Notepad++'s JSTool plug-in: TreeView showing JSON

The code is structured as a TreeView extension:

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

You can call it as so:

treeView1.SetObjectAsJson(new MyNeatObject());

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.