Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

A simple JSON Editor

4.88/5 (11 votes)
14 Nov 2011CPOL4 min read 84.5K   3.6K  
Builds a parse tree from a file based JSON string and allows you to edit as a tree or a string

Introduction

This project describes a simple JSON string editor. It also describes a simple use of various useful windows controls - TreeView, MenuBar and Context menu.

Having derived great utility from this site for a considerable amount of time, it is nice to be able to finally give something back, however simple.

Background

I have been steadily moving to JSON as both a file storage paradigm and as the basis for a shared object paradigm between heterogeneous systems - in particular Desktop/Mobile and embedded. Keeping track of large JSON objects across multiple files is a pain using a text editor, so I finally decided to write a JSON file editor.

Searches did not turn up any similar projects on CodeProject and though this rendition does not go nearly far enough, it is a working first cut.

Using the Code

The code is relatively straight forward. One form, two tabs supporting a TreeView and a TextView. Switching tabs converts a JSON string into a JSON parse tree and vice versa. The user can edit either the tree view or the text view.

Image 1

Image 2

I used a JSON parser based on Mehdi Gholam's project with a couple of tweaks for a utility fix and even more speed.

In addition to basic tweaks, I also added a set of basic JSON value classes. These are necessary in order to preserve the JSON types across repeated serializing/deserialize operations.

For JSON spec see: www.json.org. A JSON string defines either:

  • A JSON object, a collection of name/value pairs where 'name' is a string and 'value' is any JSON value, OR
  • A JSON array, an ordered list of JSON values.

There are 7 defined JSON values:

  • string, number, true, false, null, object, array

Of these, string, number, true, false, null may be considered as 'basic values' and object and array as 'compound values'.

Compound values are a structured collection of JSON values, for example:

{ "name" : "this is the value" }
{ "name" : 123.45 }
{ "name" : { "object" : 123 } }
[ "string", 1234, true, false, null, { "object" : 123 } ]
{ "name" : [ "string", 1234, true, false, null, { "object" : 123 } ] }

Mehdi's original code converted all basic JSON values to C# type 'string' and the original JSON type info was lost. When serializing/deserializing, it is sometimes important not to lose track of the basic value type and thus I extended the JSON.cs implementation to retain the original value type information. Doing so comes at the cost of a very slight loss of throughput in order to create the derived objects.

The main additions are a set of basic class types in JSON.cs as follows:

C#
public class jsonValue
public class jsonBoolean : jsonValue
public abstract class jsonNumber : jsonValue
public class jsonInteger : jsonNumber
public class jsonReal : jsonNumber

The basic value 'null' is handled as a jsonValue with no derived type.

These classes have a set of implicit operators to allow code such as:

C#
jsonInteger a = 1;
jsonInteger b = "1";
jsonNumber c = "1";
jsonNumber d = 1;
jsonNumber e = 1.234;
jsonNumber f = "1.234";
jsonReal g = 1.5;
jsonReal h = "1.6";
string s = (string) a;
string s1 = (string) d;
string s2 = (string) g;

JSON requires that all string values be quoted, however any other values remains unquoted. Thus, in addition to the standard ToString() methods, each class also has an Emit() method to allow the value to be emitted correctly as a JSON string.

ToString() is used internally to review the contents of a value and Emit() is used to emit the changed data into the JSON derived string. Here are the two methods for jsonString:

public override string ToString()
{
    return value.ToString();
}

public override string Emit()
{
    return "\"" + value + "\"";
} 

All other details are handled in the MainForm code. This is not the most elegant, but it is the most transparent. There is only one trick to the tree processing, ArrayList nodes are handled like key/value pairs without a key. Further, the Node.Tag field is used to store all objects regardless of type. However, as C# objects, JSON objects and JSON ArrayLists look the same and with only one Tag field, I really should have figured a neater way to handle the ambiguity. As it is, the Node.Text field is used to disambiguate and this is somewhat obscure.

The worst example is probably scanTree() which scans the tree to emit the JSON string:

C#
private void scanTree(TreeNode t)
  {
  foreach (TreeNode o in t.Nodes)
        {
        if (o.Nodes.Count == 0)
           {
           // leaf node
           for (int i = 0; i < indentCount; i++)
              sb.Append(indentString);
              
           // figure object (named) or array (no name)
           string[] s = o.Text.Split(':');
           if ((s[0] = s[0].Trim()) != "")
              // not an array, so emit the name
              sb.Append("\"" + s[0] + "\" : ");
              
           // emit the value string
           sb.Append(((jsonValue)o.Tag).Emit());
           endLine(",");
           }
        else
           {
           newLine("\"" + o.Text + "\" : ");
           indent((string)o.Tag);
           scanTree(o);
           dedent(((string)o.Tag)[0] == '[' ? "]," : "},");
           }
        }
   } 

As you can see, it uses the format of the Node.Text string to disambiguate an object (always has a name) from an array (never has a name).

Maybe someone can suggest a more elegant way to handle this - maybe continue the derived classes to include jsonObject and jsonArray?

Points of Interest

I am mostly an embedded systems guy coding C. Each time I visit C#, it is invariably to get something done to facilitate the operations of an embedded device. This tends to lead to very ad-hoc knowledge of the language that gets moved 10 steps forward by mad cookbook page turning and 9 steps backward as I forget it all.

History

  • V1.00 November 2011 - First cut

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)