C#实现超级简单和⾼效的JSON解析器
是使⽤C#实现的JSON解析器 ,算法思路来⾃于游戏引擎Mojoc的C语⾔实现。借助C#的类库,可以⽐C的实现更加的简洁和全⾯,尤其是处理Unicode转义字符(\u开头)的解析,C#的StringBuilder本⾝就⽀持了Unicode码点(code point)。
MojoUnityJson使⽤递归下降的解析模式,核⼼解析代码只有450⾏(去掉空⾏可能只有300多⾏),⽀持标准的JSON格式。算法实现⼒求简洁明了,⽤最直接最快速的⽅法达到⽬的,没有复杂的概念和模式。除了解析JSON,还提供了⼀组⽅便直观的API来访问JSON数据,整体实现只有⼀个⽂件,仅依赖System.Collections.Generic,System.Text,System 三个命名空间,MojoUnityJson可以很容易的嵌⼊到其它项⽬⾥使⽤。
本⽂主要介绍⼀下,超级简单⼜⾼效,并且看⼀眼就完全明⽩的解析算法,⼏乎可以原封不动的复制粘贴成其它语⾔版本的实现。
保存上下⽂信息
使⽤⼀个简单的结构体,⽤来在解析的过程中,传递⼀些上下⽂数据。
private struct Data
{
// 需要解析的JSON字符串
public string        json;
// 当前JSON字符串解析的位置索引
public int          index;
// 缓存⼀个StringBuilder,⽤来抠出JSON的⼀段字符。
public StringBuilder sb;
public Data(string json, int index)
{
this.json  = json;
this.index = index;
this.sb    = new StringBuilder();
}
}
抽象JSON的值
我们把JSON的值抽象成以下⼏个类型:
public enum JsonType
{
Object,
Array,
String,
Number,
Bool,
Null,
}
整体解析步骤
// 解析 JsonValue
private static JsonValue ParseValue(ref Data data);
// 解析 JsonObject
private static JsonValue ParseObject(ref Data data);
// 解析 JsonArray
private static JsonValue ParseArray(ref Data data);
// 解析 string
private static JsonValue ParseString(ref Data data);
// 解析 number
private static JsonValue ParseNumber(ref Data data)
这就是全部的解析流程,在ParseValue中会根据字符判断类型,分别调⽤下⾯⼏个不同的解析函数。JsonValue就对应⼀个JSON的值,它有⼀个JsonType代表了这个值的类型。这是⼀个递归的过程,在ParseValue,ParseObject和ParseArray过程中,会递归的调⽤ParseValue。JSON⼀定是始于⼀个,Object或Array,当这个最顶层的值解析完毕的时候,整个JSON也就解析完成了。
解析空⽩字符
解析过程中,会有很多为了格式化存在的空⽩字符,需要剔除这些,才能获得有信息的字符,这是⼀个重复的过程,需要⼀个函数统⼀处理。
private static void SkipWhiteSpace(ref Data data)
{
while (true)
{
switch (data.json[data.index])
{
case ' ' :
case '\t':
case '\n':
case '\r':
// 每次消耗⼀个字符,就向后推进JSON的索引
data.index++;
continue;
}
break;
}
}
解析JsonValue
private static JsonValue ParseValue(ref Data data)
{
// 跳过空⽩字符
SkipWhiteSpace(ref data);
var c = data.json[data.index];
switch (c)
{
case '{':
// 表⽰Object
return ParseObject(ref data);
case '[':
// 表⽰Array
return ParseArray (ref data);
return ParseArray (ref data);
case '"':
// 表⽰string
return ParseString(ref data);
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case '-':
json值的类型有哪些// 表⽰数值
return ParseNumber(ref data);
case 'f': // 表⽰可能是false
if
(
data.json[data.index + 1] == 'a' &&
data.json[data.index + 2] == 'l' &&
data.json[data.index + 3] == 's' &&
data.json[data.index + 4] == 'e'
)
{
data.index += 5;
/
/ 表⽰是false
return new JsonValue(JsonType.Bool, false);
}
break;
case 't': // 表⽰可能是true
if
(
data.json[data.index + 1] == 'r' &&
data.json[data.index + 2] == 'u' &&
data.json[data.index + 3] == 'e'
)
{
data.index += 4;
// 表⽰是true
return new JsonValue(JsonType.Bool, true);
}
break;
case 'n': // 表⽰可能是null
if
(
data.json[data.index + 1] == 'u' &&
data.json[data.index + 2] == 'l' &&
data.json[data.index + 3] == 'l'
)
{
data.index += 4;
// 表⽰可能是null
return new JsonValue(JsonType.Null, null);
}
break;
}
// 不能处理了
throw new Exception(string.Format("Json ParseValue error on char '{0}' index in '{1}' ", c, data.index));
throw new Exception(string.Format("Json ParseValue error on char '{0}' index in '{1}' ", c, data.index));
}
ParseValue是解析的主⼊⼝,代表着解析JsonValue这个抽象的JSON值,其真实的类型在解析的过程中逐渐具体化。
在剥离掉空⽩字符之后,就可以很容易的通过单个字符,就判断出其可能的数值类型,⽽不需要向前或向后检索更多的字符。
true,false,null 这⼏个固定的类型,直接就处理掉了,⽽其它稍微复杂的类型需要使⽤函数来处理。
这⾥没有使⽤if else,⽽是⼤量使⽤了case,是为了提⾼效率,减少判断次数。
解析JsonObject
private static JsonValue ParseObject(ref Data data)
{
/
/ Object 对应 C#的Dictionary
var jsonObject = new Dictionary<string, JsonValue>(JsonObjectInitCapacity);
// skip '{'
data.index++;
do
{
// 跳过空⽩字符
SkipWhiteSpace(ref data);
if (data.json[data.index] == '}')
{
// 空的Object, "{}"
break;
}
DebugTool.Assert
(
data.json[data.index] == '"',
"Json ParseObject error, char '{0}' should be '\"' ",
data.json[data.index]
);
// skip '"'
data.index++;
var start = data.index;
/
/ 解析Object的key值
while (true)
{
var c = data.json[data.index++];
switch (c)
{
case '"':
// check end '"'
break;
case '\\':
// skip escaped quotes
data.index++;
continue;
default:
continue;
}
// already skip the end '"'
// already skip the end '"'
break;
}
// get object key string
// 抠出key字符串
var key = data.json.Substring(start, data.index - start - 1);
// 跳过空⽩
SkipWhiteSpace(ref data);
DebugTool.Assert
(
data.json[data.index] == ':',
"Json ParseObject error, after key = {0}, char '{1}' should be ':' ",
key,
data.json[data.index]
);
// skip ':'
data.index++;
// set JsonObject key and value
// 递归的调⽤ParseValue获得Object的value值
jsonObject.Add(key, ParseValue(ref data));
// 跳过空⽩
SkipWhiteSpace(ref data);
if (data.json[data.index] == ',')
{
// Object的下⼀对KV
data.index++ ;
}
else
{
// 跳过空⽩
SkipWhiteSpace(ref data);
DebugTool.Assert
(
data.json[data.index] == '}',
"Json ParseObject error, after key = {0}, char '{1}' should be '{2}' ",
key,
data.json[data.index],
'}'
)
;
break;
}
}
while (true);
// skip '}' and return after '}'
data.index++;
return new JsonValue(JsonType.Object, jsonObject);
}
JsonObject类型就简单的对应C#的Dictionary,value是JsonValue类型。当解析完成后,value的类型就是确定的了。
JsonValue是递归的调⽤ParseValue来处理的,其类型可能是JsonType枚举的任意类型。
解析JsonArray

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。