Json-Tutorial01 特殊字面量解析——false/true/null
1. 简介
本栏目的内容来自于一个轻量级的json解析库设计教程:https://github.com/miloyip/json-tutorial,主要根据其中的tutorial来介绍设计的逻辑。下面来看一个简单的json文本:
{
"title": "Design Patterns",
"subtitle": "Elements of Reusable Object-Oriented Software",
"author": [
"Erich Gamma",
"Richard Helm",
"Ralph Johnson",
"John Vlissides"
],
"year": 2009,
"weight": 1.8,
"hardcover": true,
"publisher": {
"Company": "Pearson Education",
"Country": "India"
},
"website": null
}
从此例子可看出,JSON 是树状结构,而 JSON 只包含 6 种数据类型:
- null: 表示为 null
- boolean: 表示为 true 或 false
- number: 一般的浮点数表示方式,在下一单元详细说明
- string: 表示为 "..."
- array: 表示为 [ ... ]
- object: 表示为
我们要实现的 JSON 库,主要是完成 3 个需求:
- 把 JSON 文本解析为一个树状数据结构(parse)。
- 提供接口访问该数据结构(access)。
- 把数据结构转换成 JSON 文本(stringify)

2. 字面量解析
本节主要完成三个字面量false/true/null的解析,比较简单。主要的解析函数是lept_parse_value。这个函数会检测json字符数组的第一个字符,如果为n就按照null解析,f和t以此类推。
static int lept_parse_value(lept_context* c, lept_value* v) {
switch (*c->json) {
case 'n': return lept_parse_null(c, v);
case '\0': return LEPT_PARSE_EXPECT_VALUE;
case 'f': return lept_parse_false(c, v);
case 't': return lept_parse_true(c, v);
default: return LEPT_PARSE_INVALID_VALUE;
}
}
这里lept_cntext的结构其实就是要解析的字符数组:
typedef struct {
const char* json;
}lept_context;
lept_value是解析出来的结果,即我们常说的Json Value。lept_type是一个枚举类。这里lept_value为啥只有一个type呢,因为我们第一节只解析三个字面量,true/false/null,他们是没有值的,只用类型就可以表示。
typedef struct {
lept_type type;
}lept_value;
typedef enum { LEPT_NULL, LEPT_FALSE, LEPT_TRUE, LEPT_NUMBER, LEPT_STRING, LEPT_ARRAY, LEPT_OBJECT } lept_type;
2.1 lept_parse_xxx
解析true/false/null这三个字面量的逻辑差不多,以null为例,我们写一个函数lept_parse_null。
static int lept_parse_null(lept_context* c, lept_value* v) {
EXPECT(c, 'n');
if (c->json[0] != 'u' || c->json[1] != 'l' || c->json[2] != 'l')
return LEPT_PARSE_INVALID_VALUE;
c->json += 3;
v->type = LEPT_NULL;
return LEPT_PARSE_OK;
}
解析null时,从第二个字母开始判断,依次为u、l、l则解析成功。如果有一个字母不满足要求,则直接return LEPT_PARSE_INVALID_VALUE,短路后面的判断。如果往后的三个字母都满足要求(ull),则将字符数粗指针往后移动3。v->type设置为null,解析成功。
2.2 多root的情况
如果仅仅是这么修改,是无法通过全部的test用例的。来看一个例子:
static void test_parse_root_not_singular() {
lept_value v;
v.type = LEPT_FALSE;
EXPECT_EQ_INT(LEPT_PARSE_ROOT_NOT_SINGULAR, lept_parse(&v, "null x"));
EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));
}
这里解析的json字符串是null x。如果按照之前lept_parse_null的逻辑,解析完null之后就算是成功了。实际上null后面还跟着一个空格和x,这是不合法的。所以我们在解析完null之后,还得看下空格后面有没有字符。
int lept_parse(lept_value* v, const char* json) {
/*........*/
lept_parse_whitespace(&c); /*去除前置空格*/
if ((ret = lept_parse_value(&c, v)) == LEPT_PARSE_OK) {
/*对字面量解析完成后,跳过后面的空格*/
lept_parse_whitespace(&c);
/*r如果空格后面的第一个字符不是结束符,则说明有多个root,解析失败*/
if (*(c.json) != '\0') {
return LEPT_PARSE_ROOT_NOT_SINGULAR;
}
}
return ret;
}
这样,就可以通过所有的用例了。
浙公网安备 33010602011771号