65.9K
CodeProject 正在变化。 阅读更多。
Home

简单的JSON编辑器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.88/5 (9投票s)

2011年11月14日

CPOL

4分钟阅读

viewsIcon

86699

downloadIcon

3647

从基于文件的JSON字符串构建解析树,并允许您以树形或字符串形式进行编辑。

引言

本项目描述了一个简单的 JSON string 编辑器。它还描述了各种有用窗口控件的简单用法 - TreeView、MenuBar 和 Context menu。

在很长一段时间里,我一直从这个网站获得了极大的便利,现在终于能够回馈一些东西,即使它很简单。

背景

我一直在逐步转向 JSON,既作为文件存储范式,也作为异构系统(特别是桌面/移动和嵌入式系统)之间共享对象范式的基础。使用文本编辑器跟踪多个文件中的大型 JSON 对象非常麻烦,所以我最终决定编写一个 JSON 文件编辑器。

搜索在 CodeProject 上没有找到类似的工程,尽管这个版本还有很长的路要走,但它是第一个可用的版本。

Using the Code

代码相对简单。一个窗体,两个标签页支持 TreeView TextView。切换标签页可以将 JSON string 转换为 JSON 解析树,反之亦然。用户可以编辑树视图或文本视图。

我使用了基于 Mehdi Gholam 的项目的 JSON 解析器,并进行了一些调整,以获得 实用性修复,甚至 更多速度

除了基本调整之外,我还添加了一组基本的 JSON 值类。这些类对于在重复的序列化/反序列化操作中保留 JSON 类型至关重要。

有关 JSON 规范,请参阅:www.json.org。JSON string 定义了:

  • JSON 对象,一组名称/值对,其中“name”是 string ,“value”是任何 JSON 值,或者
  • JSON 数组,一个有序的 JSON 值列表。

有 7 种定义的 JSON 值:

  • stringnumbertruefalsenullobjectarray

其中,stringnumbertruefalsenull 可以被视为“基本值”,而 object array 被视为“复合值”。

复合值是 JSON 值的结构化集合,例如:

    { "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 的原始代码将所有基本 JSON 值转换为 C# 类型 'string',并且原始 JSON 类型信息丢失了。在序列化/反序列化时,有时重要的是不要丢失基本值类型的跟踪,因此我扩展了 *JSON.cs* 实现以保留原始值类型信息。这样做会以极低的吞吐量损失为代价来创建派生对象。

主要的添加是在 *JSON.cs* 中的一组基本类类型,如下所示:

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

基本值 'null' 被处理为没有派生类型的 jsonValue

这些类具有一组隐式运算符,允许像这样编写代码:

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 要求所有 string 值都必须用引号括起来,但任何其他值都保持不带引号。因此,除了标准的 ToString() 方法外,每个类还有一个 Emit() 方法,允许值正确地作为 JSON string 输出。

ToString() 用于内部检查值的内容,而 Emit() 用于将更改的数据输出到 JSON 派生的 string。以下是 jsonString 的两个方法:

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

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

所有其他细节都在 MainForm 代码中处理。这并非最优雅,但最透明。树处理只有一个技巧,ArrayList 节点被像键值对一样处理,但没有键。此外,Node.Tag 字段用于存储所有对象,无论其类型如何。然而,作为 C# 对象,JSON 对象和 JSON ArrayLists 看起来是相同的,并且只有一个 Tag 字段,我确实应该找到一种更简洁的方法来处理这种歧义。目前,Node.Text 字段用于区分,这有点晦涩。

最糟糕的例子可能是 scanTree() ,它扫描树以输出 JSON string

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] == '[' ? "]," : "},");
           }
        }
   } 

如您所见,它使用 Node.Text string 的格式来区分对象(总是有名称)和数组(从没有名称)。

也许有人可以建议一种更优雅的方法来处理这个问题 - 也许继续派生类以包括 jsonObject jsonArray

关注点

我主要是嵌入式系统方面的 C 语言开发者。每次我接触 C#,都是为了完成一些有助于嵌入式设备操作的事情。这往往导致对该语言的非常临时的知识,通过疯狂地翻阅食谱向前推进 10 步,又因为我忘记了这一切而向后退 9 步。

历史

  • V1.00 2011 年 11 月 - 初稿
© . All rights reserved.