第五节主要内容是解析数组,难点是如何循环递归解析数组里的内容,因为数组里的元素也都是json对象,也可能是数组;其次是如何存储数组元素。
leptjson.h
json对象中引入数组部分,包括一个json对象数组和size。
//typedef struct lept_value lept_value;
//由于 lept_value 内使用了自身类型的指针,我们必须前向声明(forward declare)此类型。
// VS中不加似乎也可以
struct lept_value {
union {
struct { lept_value* e; size_t size; } a;
struct { char* s; size_t len; } s;
double n;
}u;
lept_type type;
};
新的解析结果,解析函数返回值
enum {
LEPT_PARSE_OK = 0,
LEPT_PARSE_EXPECT_VALUE,
LEPT_PARSE_INVALID_VALUE,
LEPT_PARSE_ROOT_NOT_SINGULAR,
LEPT_PARSE_NUMBER_TOO_BIG,
LEPT_PARSE_MISS_QUOTATION_MARK,
LEPT_PARSE_INVALID_STRING_ESCAPE,
LEPT_PARSE_INVALID_STRING_CHAR,
LEPT_PARSE_INVALID_UNICODE_HEX,
LEPT_PARSE_INVALID_UNICODE_SURROGATE,
LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET // 缺少逗号或者方括号
};
leptjson.cpp
static int lept_parse_array(lept_context* c, lept_value* v) {
size_t i, size = 0;
int ret;
EXPECT(c, '['); // 识别到方括号
lept_parse_whitespace(c); // 去除空值符号
if (*c->json == ']') { // 如果识别到右方括号,直接解析结束,数组指针指向空
c->json++;
v->type = LEPT_ARRAY;
v->u.a.size = 0;
v->u.a.e = NULL;
return LEPT_PARSE_OK;
}
for (;;) { // 挨个解析数组元素(可能是任何json对象)
lept_value e; // 重新初始化个json对象用来存储解析时候的临时变量
lept_init(&e);
if ((ret = lept_parse_value(c, &e)) != LEPT_PARSE_OK) { // 解析元素中的对象失败
break;
// 不能直接返回ret,要free后return,防止内存泄露
}
//printf("************%d\n", sizeof(lept_value));
//printf("************%d\n", sizeof(lept_value::u));
//printf("************%d\n", sizeof(lept_value::u.n));
//printf("************%d\n", sizeof(lept_value::u.a));
//printf("************%d\n", sizeof(lept_value::u.s));
//printf("************%d\n", sizeof(lept_value::u.s.len));
//printf("************%d\n", sizeof(lept_value::u.s.s));
//printf("************%d\n\n", sizeof(lept_value::type));
// 识别成功,则将临时变量存进json语句的stack中
// 尽管每个json对象的种类可能不同,但由于按最大需要的内存进行存储,所以不会发生冲突
// 比如数字只需要8字节(变量+类型);但字符串和数组都需要16字节(指针+大小+类型)
// 但存储变量也用16字节,所以不需要考虑存储不同json类型时候的问题
// 都是直接存16个字节的指针
memcpy(lept_context_push(c, sizeof(lept_value)), &e, sizeof(lept_value)); // 任意类型均可
size++;
lept_parse_whitespace(c);
if (*c->json == ',') { // 遇到逗号, 继续解析
c->json++;
lept_parse_whitespace(c);
}
else if (*c->json==']') { // 解析结束
c->json++;
v->type = LEPT_ARRAY;
v->u.a.size = size;
size *= sizeof(lept_value);
// 解析结束后,将json语句中的stack存进json对象中数组的指针里
memcpy(v->u.a.e = (lept_value*)malloc(size), lept_context_pop(c, size), size);
//printf("%d\n", lept_get_type(lept_get_array_element(v, 1)));
return LEPT_PARSE_OK;
}
else {
// 解析完一个元素后,缺少逗号和中括号
ret = LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET;
break;
}
}
for (i = 0; i < size; i++)
// 依次释放json对象内存
lept_free((lept_value*)lept_context_pop(c, sizeof(lept_value)));
return ret;
}
json语句中数组解析到json语句的栈,再从栈到json数组对象的指针,最后从指针里取对象
(这种指针操作我服气,太牛了)
向context指向的栈存数据
static void* lept_context_push(lept_context* c, size_t size) {
void* ret;
assert(size > 0);
if (c->top + size >= c->size) {
if (c->size == 0) {
c->size = LEPT_PARSE_STACK_INIT_SIZE;
}
while (c->top + size >= c->size) {
c->size += c->size >> 1;
}
c->stack = (char*)realloc(c->stack, c->size);
}
ret = c->stack + c->top;
c->top += size;
return ret;
}
// 存json对象到json语句中的栈中
//个人理解:不知道对不对
// 这种存法,可以避免push时候,重新开辟栈空间,指针变成悬挂指针。
// 不用先lept_value* e = lept_context_push(c, sizeof(lept_value))
// lept_parse_value(c, &e)时候,e可能会被改变
memcpy(lept_context_push(c, sizeof(lept_value)), &e, sizeof(lept_value)); // 任意类型均可
// 从栈中取指针到json对象
memcpy(v->u.a.e = (lept_value*)malloc(size), lept_context_pop(c, size), size);
// 从后往前依次释放json值内存
for (i = 0; i < size; i++)
lept_free((lept_value*)lept_context_pop(c, sizeof(lept_value)));
// 按照index从json数组对象中取json对象
lept_value* lept_get_array_element(const lept_value*v,size_t index) {
assert(v != NULL && v->type == LEPT_ARRAY);
assert(index < v->u.a.size);
return &v->u.a.e[index];
}
test.cpp
无效值
TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "[1,]"); // 不能有逗号
TEST_ERROR(LEPT_PARSE_INVALID_VALUE, "[\"a\", nul]"); // 最后一个值解析失败
缺少逗号、括号
TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, "[1");
TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, "[1}");
TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, "[1 2");
TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_SQUARE_BRACKET, "[[]");