每一节都按照三个文件分别介绍代码思路和逻辑,叶大佬的教程每一章都包括主题内容和解答两部分,本专栏在介绍时候合二为一,介绍补充完答案后的内容。
第一节是解析布尔类型:
leptjson.h
首先是json的数据类型:包括空,布尔,浮点数,字符串,数组,对象
用一个枚举类型lept_type表示
null: 表示为 null
boolean : 表示为 true 或 false
number : 浮点数
string : 表示为 "abc"
array : 表示为 [null,true,number,string,[,,]]
object : 表示为{"k1":null,"k2":true,"k3":123,"k4":"value4","k5":[1,2],"k6":{}}
typedef enum {
LEPT_NULL, LEPT_FALSE, LEPT_TRUE, LRPT_NUMBER, LEPT_STRING, LEPT_ARRAY, LEPT_OBJECT
} lept_type;
lept_value为json值(可以理解为json对象),由于第一章只检测空和布尔,所以里面只有对象类型(空和布尔类型的值即为他们的类型) 后续会加入字符串的内容,浮点数的数值,数组的成员、大小、容量,对象的成员、大小、容量,
typedef struct {
lept_type type;
}lept_value;
解析器解析结果,前几章都是一个个检测,即一次检测只能检测一种类型,不能检测复合的json对象(第六章介绍)
一开始只能检测"123"是数字,"true"是布尔,""abc""是字符串
enum {
LEPT_PARSE_OK = 0, // 无错误
LEPT_PARSE_EXPECT_VALUE, // 空, 如 " ",""
LEPT_PARSE_INVALID_VALUE, // 无效值,检测结果不是null,true,false,
LEPT_PARSE_ROOT_NOT_SINGULAR // 结果后面还有其他值,"null n"
};
int lept_parse(lept_value* v, const char* json); // 解析函数,传入字符串,解析出json对象
lept_type lept_get_type(const lept_value* v);
// 访问解析结果,这个只访问对象类型:如"null"为LEPT_NULL,"123"为LRPT_NUMBER
leptjson.cpp
如果第一个字符是自己想要的,则指针移动一步
#define EXPECT(c, ch) do{assert(*c->json==(ch));c->json++;}while(0)
// json语句,字符串
typedef struct {
const char* json;
}lept_context;
解析空白(空格 制表 换行 回车),遇到空格、制表、换行、回车,指针后移一步
static void lept_parse_whitespace(lept_context* c) {
const char* p = c->json;
while (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r') {
p++;
}
c->json = p;
}
解析LEPT_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; // 如果不是null,则是无效值
}
c->json += 3; // 是null;
v->type = LEPT_NULL;
return LEPT_PARSE_OK;
}
解析值函数:
遇到 n 尝试解析LEPT_NULL类型
t ➔ true
f ➔ false
" ➔ string
0 - 9 / -➔ number
[➔ array
{ ➔ object
static int lept_parse_value(lept_context* c, lept_value* v) {
switch (*c->json) {
case 'n': return lept_parse_null(c, v);
case 't': return lept_parse_true(c, v);
case 'f': return lept_parse_false(c, v);
case '\0': return LEPT_PARSE_EXPECT_VALUE; // 文件结尾
default: return LEPT_PARSE_INVALID_VALUE; // 目前还没加入字符串,浮点数,数组,对象;如果不是第一个字符上述。则是无效值
}
}
解析函数
字符串先去除空白,再检查是不是true、false、null,如果是的话,再去除空白,再检查后面有没有字符; 如果有,则解析失败,错误类型为LEPT_PARSE_ROOT_NOT_SINGULAR,如"null n"
目前只能解析单个类型,无法解析复合类型,所以只会lept_parse_value()一次
引入数组和对象后,解析完一个后还要检查后面。如[[1,2,{"key","value"}]]
int lept_parse(lept_value* v, const char* json) {
lept_context c; // 理解为json字符串
assert(v != NULL); // v不能为空,必须指向一个lept_value(可以理解为json对象)
c.json = json;
v->type = LEPT_NULL;
lept_parse_whitespace(&c);
int ret = lept_parse_value(&c, v);
if (ret == LEPT_PARSE_OK) {
lept_parse_whitespace(&c);
if (*c.json != '\0') {
ret = LEPT_PARSE_ROOT_NOT_SINGULAR;
}
}
return ret;
}
test.cpp
这里我们希望我们的解析函数可以解析出所有json字符串,不管它是否合法,合法的给出解析结果为OK,返回json值;非法的解析结果为对应的预期值即可,如解析空白,预期解析结果LEPT_PARSE_EXPECT_VALUE,但json值里类型为null;只要解析的结果与预期相符合,就说明我们函数写对了。
尝试解析"null",解析成功,类型为LEPT_NULL
static void test_parse_null() {
lept_value v;
v.type = LEPT_FALSE;
EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, "null"));
EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));
}
尝试解析""空白,解析结果是LEPT_PARSE_EXPECT_VALUE(解析结果正确,但这种应该非法(或者说是字符串)),类型为LEPT_NULL
static void test_parse_expect_value() {
lept_value v;
v.type = LEPT_FALSE;
EXPECT_EQ_INT(LEPT_PARSE_EXPECT_VALUE, lept_parse(&v, ""));
EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));
v.type = LEPT_FALSE;
EXPECT_EQ_INT(LEPT_PARSE_EXPECT_VALUE, lept_parse(&v, " "));
EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));
}
尝试解析"nul","?"等无效值空白,解析结果是LEPT_PARSE_INVALID_VALUE, 类型为LEPT_NULL
static void test_parse_invalid_value() {
lept_value v;
v.type = LEPT_FALSE;
EXPECT_EQ_INT(LEPT_PARSE_INVALID_VALUE, lept_parse(&v, "nul"));
EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));
v.type = LEPT_FALSE;
EXPECT_EQ_INT(LEPT_PARSE_INVALID_VALUE, lept_parse(&v, "?"));
EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v)); //
}
尝试解析"null x", "true x"等无效值,解析结果应该为LEPT_PARSE_ROOT_NOT_SINGULAR, 但类型应该为LEPT_NULL;
这里他的判断逻辑,解析这种正确值后面还跟有非空字符的,把类型归为正确值的类型,我个人觉得不对,应该均为LEPT_NULL
否则"true x"为LEPT_TRUE,显然不对
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));
v.type = LEPT_FALSE;
EXPECT_EQ_INT(LEPT_PARSE_ROOT_NOT_SINGULAR, lept_parse(&v, "true x"));
EXPECT_EQ_INT(LEPT_TRUE, lept_get_type(&v));
}
修正leptjson.cpp中lept_parse逻辑,检测到解析结果为LEPT_PARSE_ROOT_NOT_SINGULAR时,类型设置为LEPT_NULL(非法)
int lept_parse(lept_value* v, const char* json) {
lept_context c; // 理解为json字符串
assert(v != NULL); // v不能为空,必须指向一个lept_value(可以理解为json对象)
c.json = json;
v->type = LEPT_NULL;
lept_parse_whitespace(&c);
int ret = lept_parse_value(&c, v);
if (ret == LEPT_PARSE_OK) {
lept_parse_whitespace(&c);
if (*c.json != '\0') {
ret = LEPT_PARSE_ROOT_NOT_SINGULAR;
v->type = LEPT_NULL; // 这里加一行
}
}
return ret;
}