4

I am building an application that gets in run-time JSON message from external source.

I don't know anything about the structure of the message text.

I want to take this JSON text, render it to a tree view (or something equivalent, UI regarding), edit this JSON in that tree view that I just dynamically created, and send the text back to the source.

I really don't know where to start..Any suggestions?

dbc
  • 104,963
  • 20
  • 228
  • 340
Guy Segal
  • 953
  • 1
  • 12
  • 24

5 Answers5

16
 private void btn_Convert_MouseClick(object sender, MouseEventArgs e)
    {
        try
        {
            string json = rbt_display.Text;
            JObject obj = JObject.Parse(json);
            tvw_display.Nodes.Clear();
            TreeNode parent = Json2Tree(obj);
            parent.Text = "Root Object";
            tvw_display.Nodes.Add(parent);

        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message, "ERROR");
        }
    }
private TreeNode Json2Tree(JObject obj)
    {
        //create the parent node
        TreeNode parent = new TreeNode();
        //loop through the obj. all token should be pair<key, value>
        foreach (var token in obj)
        {
            //change the display Content of the parent
            parent.Text = token.Key.ToString();
            //create the child node
            TreeNode child = new TreeNode();
            child.Text = token.Key.ToString();
            //check if the value is of type obj recall the method
            if (token.Value.Type.ToString() == "Object")
            {
               // child.Text = token.Key.ToString();
                //create a new JObject using the the Token.value
                JObject o = (JObject)token.Value;
                //recall the method
                child = Json2Tree(o);
                //add the child to the parentNode
                parent.Nodes.Add(child);
            }
            //if type is of array
            else if (token.Value.Type.ToString() == "Array")
            {
                int ix = -1;
              //  child.Text = token.Key.ToString();
                //loop though the array
                foreach (var itm in token.Value)
                {
                    //check if value is an Array of objects
                    if (itm.Type.ToString() == "Object")
                    {
                        TreeNode objTN = new TreeNode();
                        //child.Text = token.Key.ToString();
                        //call back the method
                        ix++;

                        JObject o = (JObject)itm;
                        objTN = Json2Tree(o);
                        objTN.Text = token.Key.ToString() + "[" + ix + "]";
                        child.Nodes.Add(objTN);
                        //parent.Nodes.Add(child);
                    }
                    //regular array string, int, etc
                    else if(itm.Type.ToString() == "Array")
                    {
                        ix++;
                        TreeNode dataArray = new TreeNode(); 
                        foreach (var data in itm)
                        {
                            dataArray.Text = token.Key.ToString() + "[" + ix + "]";
                            dataArray.Nodes.Add(data.ToString());
                        }
                        child.Nodes.Add(dataArray);   
                    }

                    else
                    {
                        child.Nodes.Add(itm.ToString());
                    }
                }
                parent.Nodes.Add(child);
            }
            else
            {
                //if token.Value is not nested
               // child.Text = token.Key.ToString();
                //change the value into N/A if value == null or an empty string 
                if (token.Value.ToString() == "")
                    child.Nodes.Add("N/A");
                else
                    child.Nodes.Add(token.Value.ToString());
                parent.Nodes.Add(child);
            }
        }
        return parent;

    }
sample json
{
"firstName": "John",
"lastName": "Smith",
"isAlive": true,
"age": 25,
"height_cm": 167.6,
"address": {
"streetAddress": "21 2nd Street",
"city": "New York",
"state": "NY",
"postalCode": "10021-3100"
},
"phoneNumbers": [
{
  "type": "home",
  "number": "212 555-1234"
},
{
  "type": "office",
  "number": "646 555-4567"
}
],
"children": [],
"spouse": null
}

Note: This example uses NewtonSoft Json. Right-click solution and click manage NuGet packages to install the reference.

WonderWorker
  • 8,539
  • 4
  • 63
  • 74
user4712920
  • 176
  • 1
  • 4
  • Thank you for posting this! Found a small bug in the section under `if (token.Value.Type.ToString() == "Object")`. The parent node is named whatever the last child node is named. I added `child.Text = token.Key.ToString();` right after `child = Json2Tree(o);`. It is a little hacky, but it does the job. – bruestle2 Apr 19 '16 at 19:27
  • This helped me with a solution to show complex json in a "pretty" view for end users - analysts and data enterers. Thank you! – Frank Silano May 04 '20 at 17:20
4

This code will handle both JArray or JObject as an input:

        string jsonString = "your json string here";
        string rootName = "root", nodeName = "node";
        JContainer json;
        try {
            if (jsonString.StartsWith("["))
            {
                json = JArray.Parse(jsonString);
                treeView1.Nodes.Add(Utilities.Json2Tree((JArray)json, rootName, nodeName));
            }
            else
            {
                json = JObject.Parse(jsonString);
                treeView1.Nodes.Add(Utilities.Json2Tree((JObject)json, text));
            }
        }
        catch(JsonReaderException jre)
        {
            MessageBox.Show("Invalid Json.");
        }

public class Utilities
{

    public static TreeNode Json2Tree(JArray root, string rootName = "", string nodeName="")
    {
        TreeNode parent = new TreeNode(rootName);
        int index = 0;

        foreach(JToken obj in root)
        {
            TreeNode child = new TreeNode(string.Format("{0}[{1}]", nodeName, index++));
            foreach (KeyValuePair<string, JToken> token in (JObject)obj)
            {
                switch (token.Value.Type)
                {
                    case JTokenType.Array:
                    case JTokenType.Object:
                        child.Nodes.Add(Json2Tree((JObject)token.Value, token.Key));
                        break;
                    default:
                        child.Nodes.Add(GetChild(token));
                        break;
                }
            }
            parent.Nodes.Add(child);
        }

        return parent;
    }

    public static TreeNode Json2Tree(JObject root, string text = "")
    {
        TreeNode parent = new TreeNode(text);

        foreach (KeyValuePair<string, JToken> token in root)
        {

            switch (token.Value.Type)
            {
                case JTokenType.Object:
                    parent.Nodes.Add(Json2Tree((JObject)token.Value, token.Key));
                    break;
                case JTokenType.Array:
                    int index = 0;
                    foreach(JToken element in (JArray)token.Value)
                    {
                        parent.Nodes.Add(Json2Tree((JObject)element, string.Format("{0}[{1}]", token.Key, index++)));
                    }

                    if (index == 0) parent.Nodes.Add(string.Format("{0}[ ]", token.Key)); //to handle empty arrays
                    break;
                default:
                    parent.Nodes.Add(GetChild(token));
                    break;
            }
        }

        return parent;
    }

    private static TreeNode GetChild(KeyValuePair<string, JToken> token)
    {
        TreeNode child = new TreeNode(token.Key);
        child.Nodes.Add(string.IsNullOrEmpty(token.Value.ToString()) ? "n/a" : token.Value.ToString());
        return child;
    }

}
vinceg
  • 81
  • 3
  • I used this code as a basis of code I created. There is an issue with trying to cast a `JArray` to a `JObject`. In fixing the issue, I discovered that the one of your methods could be removed which left two. I used it in WPF, but the code is similar since it is a base; for anyone else needed the latest version -> My answer can be seen here [How to display JSON in WPF TreeView](https://stackoverflow.com/a/59431426/285795) – ΩmegaMan Dec 20 '19 at 21:23
1

You can try this code :

public class JsonTag
{
    public JsonTag(JsonReader reader)
    {
        TokenType = reader.TokenType;
        Value = reader.Value;
        ValueType = reader.ValueType;
    }

    public JsonToken TokenType { get; set; }
    public object Value { get; set; }
    public Type ValueType { get; set; }
}
private void JsonToTreeview(string json)
{
    tvwValue.BeginUpdate();
    var parentText = string.Empty;

    TreeNodeCollection parentNodes = tvwValue.Nodes;
    TreeNode current = null;
    tvwValue.Nodes.Clear();
    var reader = new JsonTextReader(new StringReader(json));
    while (reader.Read())
    {
        switch (reader.TokenType)
        {
            case JsonToken.None:
                break;
            case JsonToken.StartObject:
                current = new TreeNode("{}") { Tag = new JsonTag(reader) };

                parentNodes.Add(current);
                parentNodes = current.Nodes;
                break;
            case JsonToken.StartArray:
                current = new TreeNode("[]") { Tag = new JsonTag(reader) };

                parentNodes.Add(current);

                if (current.PrevNode != null)
                {
                    if (((JsonTag)current.PrevNode.Tag).TokenType == JsonToken.PropertyName)
                        current.Parent.Text += "[]";

                    parentText = current.Parent.Text;
                    if (current.Parent.Parent.Text.Length > 2)
                        parentText = ", " + parentText;
                    current.Parent.Parent.Text = current.Parent.Parent.Text.Insert(current.Parent.Parent.Text.Length - 1, parentText);
                }

                parentNodes = current.Nodes;
                break;
            case JsonToken.StartConstructor:
                break;
            case JsonToken.PropertyName:
                current = new TreeNode("\"" + reader.Value + "\" : ");
                parentNodes.Add(current);

                if (current.PrevNode != null)
                    current.PrevNode.Text += ",";

                parentNodes = current.Nodes;
                current = new TreeNode(reader.Value.ToString()) { Tag = new JsonTag(reader) };
                parentNodes.Add(current);

                break;
            case JsonToken.Comment:
                break;
            case JsonToken.Raw:
                break;
            case JsonToken.Date:
            case JsonToken.Integer:
            case JsonToken.Float:
            case JsonToken.Boolean:
            case JsonToken.String:
                var readerValue = "";
                if (reader.TokenType == JsonToken.String)
                    readerValue = "\"" + reader.Value + "\"";
                else
                    readerValue = reader.Value.ToString();

                current = new TreeNode(readerValue) { Tag = new JsonTag(reader) };
                parentNodes.Add(current);

                current.Parent.Text += readerValue;

                parentText = current.Parent.Text;
                if (current.Parent.Parent.Text.Length > 2)
                    parentText = ", " + parentText;
                current.Parent.Parent.Text = current.Parent.Parent.Text.Insert(current.Parent.Parent.Text.Length - 1, parentText);

                if (((JsonTag)current.PrevNode.Tag).TokenType == JsonToken.PropertyName)
                    current = current.Parent;
                current = current.Parent;
                parentNodes = current.Nodes;
                break;
            case JsonToken.Bytes:
                break;
            case JsonToken.Null:
                break;
            case JsonToken.Undefined:
                break;
            case JsonToken.EndObject:
                if (current.FirstNode.Tag != null &&
                    ((JsonTag)current.FirstNode.Tag).TokenType == JsonToken.PropertyName)
                    current = current.Parent;
                current = current.Parent;
                if (current == null)
                    parentNodes = tvwValue.Nodes;
                else
                    parentNodes = current.Nodes;
                break;
            case JsonToken.EndArray:
                if (((JsonTag)current.PrevNode.Tag).TokenType == JsonToken.PropertyName)
                    current = current.Parent;
                current = current.Parent;
                if (current == null)
                    parentNodes = tvwValue.Nodes;
                else
                    parentNodes = current.Nodes;
                break;
            case JsonToken.EndConstructor:
                break;
            default:
                throw new ArgumentOutOfRangeException();
        }
    }
    tvwValue.EndUpdate();
}
sinanguler
  • 53
  • 1
  • 5
0

Lots of questions there, really. If you really need guidance on every part of that then it's a lot to try and answer here.

There are classes for reading JSON structures, readily available. As Yosi indirectly linked, there's JSON.net

Once you can read the JSON, you can use it to construct the TreeView

Editing is simple enough, as the TreeView has a property for LabelEdit that supports editing in-place. From there, it's just a matter of reacting to that and keeping track of the changes. Or perhaps reading it all back out in one fell swoop at the end, your choice. Either way, the TreeView has events such as BeforeLabelEdit, AfterLabelEdit, etc., all of which can be found on the TreeView link above.

DonBoitnott
  • 10,787
  • 6
  • 49
  • 68
  • can you be more specific about the technique of constructing the tree view from the json? I dont have a problem getting the JSON for the external source, my issue is mainly how to create the tree from the JSON in a proper way – Guy Segal Sep 13 '13 at 10:50
  • That will have a great deal to do with the structure of the data you are reading. At a minimum, I assume you will read the data into a structure, perhaps a class. You will need to identify what constitutes the "parent nodes" and what are the "child nodes", as well as to what depth you will have to nest them. With that in mind, you simply loop over the elements you derived from the Json stream and construct nodes, nesting as you go. Every node, regardless of its nesting level, has a `Nodes` property, to which you can `Add()` children. – DonBoitnott Sep 13 '13 at 12:16
0

From the package manager console:

PM> Install-Package Newtonsoft.Json

Then cleaning up @vinceg 's answer, I rolled a static class:

    using Newtonsoft.Json.Linq;
    using System.Collections.Generic;
    using System.Windows.Forms;
    
    public static class clsJSON2treeview
    {
    
        /// <summary>Parse JSON string, individual tokens become TreeView Nodes ~mwr</summary>
        /// <param name="oTV">TreeView control to display parsed JSON</param>
        /// <param name="sJSON">Incoming JSON string</param>
        /// <param name="rootName">Title of top node in TreeView wrapping all JSON</param>
        public static void JsonToTreeview(TreeView oTV, string sJSON, string rootName)
        {
            JContainer json = sJSON.StartsWith("[")
                            ? (JContainer)JArray.Parse(sJSON)
                            : (JContainer)JObject.Parse(sJSON);
    
            oTV.Nodes.Add(Ele2Node(json, rootName));
        }
    
        private static TreeNode Ele2Node(object oJthingy, string text = "")
        {
            TreeNode oThisNode = new TreeNode(text);
    
            switch (oJthingy.GetType().Name) //~mwr could not find parent object for all three JObject, JArray, JValue
            {
                case "JObject":
                    foreach (KeyValuePair<string, JToken> oJtok in (JObject)oJthingy)
                        oThisNode.Nodes.Add(Ele2Node(oJtok.Value, oJtok.Key));
                    break;
                case "JArray":
                    int i = 0;
                    foreach (JToken oJtok in (JArray)oJthingy)
                        oThisNode.Nodes.Add(Ele2Node(oJtok, string.Format("[{0}]", i++)));
    
                    if (i == 0) oThisNode.Nodes.Add("[]"); //to handle empty arrays
                    break;
                case "JValue":
                    oThisNode.Nodes.Add(new TreeNode(oJthingy.ToString()));
                    break;
                default:
                    throw new System.Exception("clsJSON2Treeview can't interpret object:" + oJthingy.GetType().Name);
            }
    
            return oThisNode;
        }
    }
mike
  • 2,149
  • 20
  • 29