开源项目cJSON具体实现6(对象的解析)

mac2022-06-30  29

文章目录

JSON 对象语法头文件测试代码函数实现

JSON 对象语法

本章实现的是JSON对象。JSON对象的实现和JSON数组的实现很是相似,我们可以对比着来看。

JSON数组JSON对象JSON数组是由JSON值value组成JSON对象是由成员对象member组成,成员对象是键值对JSON数组是 [] 构成JSON对象是 {} 构成一个数组可以包含零至多个值,而这些值可以字符,数字,也可以是数组;值与值之间以逗号分隔,例如 []、[1,2,true]、[[1,2],[3,4],“abc”] 都是合法的数组。但注意 JSON数组 不接受末端额外的逗号,例如 [1,2,] 是不合法的。JSON对象由成员对象组成,成员对象由键值对组成,其中键为 JSON字符串(string),值是任何JSON值(value),中间由冒号分割(%x3A)。

JSON对象语法。

JSON-text = ws value ws value = null / false / true / number / string / array / object object = %x7B ws [ member *( ws %x2C ws member ) ] ws %x7D member = string ws %x3A ws value

解释:

%x7B 是左大括号 {%x2C 是逗号 ,%x7D 是右大括号 }ws 是空白字符%x3A 是冒号

头文件

在 ECMA-404 标准中,并没有规定对象中每个成员的键一定要唯一的,也没有规定是否需要维持成员的次序。为了简单起见,我们选择用动态数组来实现JSON对象,对于对象,既然我们采用了动态数组的方案,那么每个对象就是成员的数组:

typedef struct lept_value lept_value; typedef struct lept_member lept_member; struct lept_value { union { struct { lept_member* m; size_t size; }o; struct { lept_value* e; size_t size; }a; struct { char* s; size_t len; }s; double n; }u; lept_type type; }; /*动态数组表示JSON对象(键值对)*/ struct lept_member { char* k; /* 键(字符串) */ size_t klen; /* 字符串的长度,因为可能包含空字符\u0000 */ lept_value v; /* 值 */ };

添加错误码:

enum { /* ... */ LEPT_PARSE_MISS_KEY, //没有键 LEPT_PARSE_MISS_COLON, //没有冒号 LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET };

同样的,添加API

size_t lept_get_object_size(const lept_value* v); const char* lept_get_object_key(conat lept_calue* v, size_t index); size_t lept_get_object_key_length(const lept_value* v, size_t index); lept_value* lept_get_object_value(const lept_value* v, size_t index);

实现API函数:

size_t lept_get_object_size(const lept_value* v) { assert(v != NULL && v->type == LEPT_OBJECT); return v->u.o.size; } const char* lept_get_object_key(const lept_value* v, size_t index) { assert(v != NULL && v->type == LEPT_OBJECT); assert(index < v->u.o.size); return v->u.o.m[index].k; } size_t lept_get_object_key_length(const lept_value* v, size_t index) { assert(v != NULL && v->type == LEPT_OBJECT); assert(index < v->u.o.size); return v->u.o.m[index].klen; } lept_value* lept_get_object_value(const lept_value* v, size_t index) { assert(v != NULL && v->type == LEPT_OBJECT); assert(index < v->u.o.size); return &v->u.o.m[index].v; }

测试代码

static void test_parse_object() { lept_value v; size_t i; lept_init(&v); EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, " { } ")); EXPECT_EQ_INT(LEPT_OBJECT, lept_get_type(&v)); EXPECT_EQ_SIZE_T(0, lept_get_object_size(&v)); lept_free(&v); lept_init(&v); EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, " { " "\"n\" : null , " "\"f\" : false , " "\"t\" : true , " "\"i\" : 123 , " "\"s\" : \"abc\", " "\"a\" : [ 1, 2, 3 ]," "\"o\" : { \"1\" : 1, \"2\" : 2, \"3\" : 3 }" " } " )); EXPECT_EQ_INT(LEPT_OBJECT, lept_get_type(&v)); EXPECT_EQ_SIZE_T(7, lept_get_object_size(&v)); EXPECT_EQ_STRING("n", lept_get_object_key(&v, 0), lept_get_object_key_length(&v, 0)); EXPECT_EQ_INT(LEPT_NULL, lept_get_type(lept_get_object_value(&v, 0))); EXPECT_EQ_STRING("f", lept_get_object_key(&v, 1), lept_get_object_key_length(&v, 1)); EXPECT_EQ_INT(LEPT_FALSE, lept_get_type(lept_get_object_value(&v, 1))); EXPECT_EQ_STRING("t", lept_get_object_key(&v, 2), lept_get_object_key_length(&v, 2)); EXPECT_EQ_INT(LEPT_TRUE, lept_get_type(lept_get_object_value(&v, 2))); EXPECT_EQ_STRING("i", lept_get_object_key(&v, 3), lept_get_object_key_length(&v, 3)); EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(lept_get_object_value(&v, 3))); EXPECT_EQ_DOUBLE(123.0, lept_get_number(lept_get_object_value(&v, 3))); EXPECT_EQ_STRING("s", lept_get_object_key(&v, 4), lept_get_object_key_length(&v, 4)); EXPECT_EQ_INT(LEPT_STRING, lept_get_type(lept_get_object_value(&v, 4))); EXPECT_EQ_STRING("abc", lept_get_string(lept_get_object_value(&v, 4)), lept_get_string_length(lept_get_object_value(&v, 4))); EXPECT_EQ_STRING("a", lept_get_object_key(&v, 5), lept_get_object_key_length(&v, 5)); EXPECT_EQ_INT(LEPT_ARRAY, lept_get_type(lept_get_object_value(&v, 5))); EXPECT_EQ_SIZE_T(3, lept_get_array_size(lept_get_object_value(&v, 5))); for (i = 0; i < 3; i++) { lept_value* e = lept_get_array_element(lept_get_object_value(&v, 5), i); EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(e)); EXPECT_EQ_DOUBLE(i + 1.0, lept_get_number(e)); } EXPECT_EQ_STRING("o", lept_get_object_key(&v, 6), lept_get_object_key_length(&v, 6)); { lept_value* o = lept_get_object_value(&v, 6); EXPECT_EQ_INT(LEPT_OBJECT, lept_get_type(o)); for (i = 0; i < 3; i++) { lept_value* ov = lept_get_object_value(o, i); EXPECT_TRUE('1' + i == lept_get_object_key(o, i)[0]); EXPECT_EQ_SIZE_T(1, lept_get_object_key_length(o, i)); EXPECT_EQ_INT(LEPT_NUMBER, lept_get_type(ov)); EXPECT_EQ_DOUBLE(i + 1.0, lept_get_number(ov)); } } lept_free(&v); } static void test_parse() { /* ... */ test_parse_object(); }

错误码的测试函数

static void test_parse_miss_key() { TEST_ERROR(LEPT_PARSE_MISS_KEY, "{:1,"); TEST_ERROR(LEPT_PARSE_MISS_KEY, "{1:1,"); TEST_ERROR(LEPT_PARSE_MISS_KEY, "{true:1,"); TEST_ERROR(LEPT_PARSE_MISS_KEY, "{false:1,"); TEST_ERROR(LEPT_PARSE_MISS_KEY, "{null:1,"); TEST_ERROR(LEPT_PARSE_MISS_KEY, "{[]:1,"); TEST_ERROR(LEPT_PARSE_MISS_KEY, "{{}:1,"); TEST_ERROR(LEPT_PARSE_MISS_KEY, "{\"a\":1,"); } static void test_parse_miss_colon() { TEST_ERROR(LEPT_PARSE_MISS_COLON, "{\"a\"}"); TEST_ERROR(LEPT_PARSE_MISS_COLON, "{\"a\",\"b\"}"); } static void test_parse_miss_comma_or_curly_bracket() { TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET, "{\"a\":1"); TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET, "{\"a\":1]"); TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET, "{\"a\":1 \"b\""); TEST_ERROR(LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET, "{\"a\":{}"); } static void test_parse() { /* ... */ test_parse_object(); test_parse_miss_key(); test_parse_miss_colon(); test_parse_miss_comma_or_curly_bracket(); }

函数实现

在函数是先前,我们先来讨论一个问题—重构

代码重构 code refactoring是指在不改变软件外在行为时,修改代码以改进结构。代码重构十分依赖于单元测试,因为我们是通过单元测试去维护代码的正确性。有了足够的单元测试,我们可以放胆去重构,尝试并评估不同的改进方式,找到合乎心意而且能通过单元测试的改动,我们才提交它。

我们知道,成员的键也是一个 JSON 字符串,然而,我们不使用 lept_value 存储键,因为这样会浪费了当中 type 这个无用的字段。由于 lept_parse_string() 是直接地把解析的结果写进一个 lept_value,所以我们先用「提取方法 」的重构方式,把解析 JSON 字符串及写入 lept_value 分拆成两部分。

先看原来的 lept_parse_string()函数:

static int lept_parse_string(lept_context* c, lept_value* v) { size_t head = c->top, len; unsigned u, u2; const char* p; EXPECT(c, '\"'); p = c->json; for (;;) { char ch = *p++; switch (ch) { case '\"': len = c->top - head; lept_set_string(v, (const char*)lept_context_pop(c, len), len); c->json = p; return LEPT_PARSE_OK; case '\\': switch (*p++) { case '\"': PUTC(c, '\"'); break; case '\\': PUTC(c, '\\'); break; case '/': PUTC(c, '/' ); break; case 'b': PUTC(c, '\b'); break; case 'f': PUTC(c, '\f'); break; case 'n': PUTC(c, '\n'); break; case 'r': PUTC(c, '\r'); break; case 't': PUTC(c, '\t'); break; case 'u': if (!(p = lept_parse_hex4(p, &u))) STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX); if (u >= 0xD800 && u <= 0xDBFF) { /* surrogate pair */ if (*p++ != '\\') STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE); if (*p++ != 'u') STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE); if (!(p = lept_parse_hex4(p, &u2))) STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX); if (u2 < 0xDC00 || u2 > 0xDFFF) STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE); u = (((u - 0xD800) << 10) | (u2 - 0xDC00)) + 0x10000; } lept_encode_utf8(c, u); break; default: STRING_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE); } break; case '\0': STRING_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK); default: if ((unsigned char)ch < 0x20) STRING_ERROR(LEPT_PARSE_INVALID_STRING_CHAR); PUTC(c, ch); } } }

这段函数有两个作用:1. 是解析字符串 2. 是把字符串保存在 lept_value里,因为JSON对象在解析时,前面键的部分也就是字符串是保存在lept_member里面,所以我们把这个函数拆开看。

/* 解析 JSON 字符串,把结果写入 str 和 len */ /* str 指向 c->stack 中的元素,需要在 c->stack */ static int lept_parse_string_raw(lept_context* c, char** str, size_t* len) { size_t head = c->top; unsigned u, u2; const char* p; EXPECT(c, '\"'); p = c->json; for (;;) { char ch = *p++; switch (ch) { case '\"': *len = c->top - head;//注意,这里得用*len,而不是len,主要是因为要改变的参数里的值 *str = lept_context_pop(c, *len); c->json = p; return LEPT_PARSE_OK; case '\\': switch (*p++) { case '\"': PUTC(c, '\"'); break; case '\\': PUTC(c, '\\'); break; case '/': PUTC(c, '/' ); break; case 'b': PUTC(c, '\b'); break; case 'f': PUTC(c, '\f'); break; case 'n': PUTC(c, '\n'); break; case 'r': PUTC(c, '\r'); break; case 't': PUTC(c, '\t'); break; case 'u': if (!(p = lept_parse_hex4(p, &u))) STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX); if (u >= 0xD800 && u <= 0xDBFF) { /* surrogate pair */ if (*p++ != '\\') STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE); if (*p++ != 'u') STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE); if (!(p = lept_parse_hex4(p, &u2))) STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_HEX); if (u2 < 0xDC00 || u2 > 0xDFFF) STRING_ERROR(LEPT_PARSE_INVALID_UNICODE_SURROGATE); u = (((u - 0xD800) << 10) | (u2 - 0xDC00)) + 0x10000; } lept_encode_utf8(c, u); break; default: STRING_ERROR(LEPT_PARSE_INVALID_STRING_ESCAPE); } break; case '\0': STRING_ERROR(LEPT_PARSE_MISS_QUOTATION_MARK); default: if ((unsigned char)ch < 0x20) STRING_ERROR(LEPT_PARSE_INVALID_STRING_CHAR); PUTC(c, ch); } } } static int lept_parse_string(lept_context* c, lept_value* v) { int ret; char* s; size_t len; if (LEPT_PARSE_OK == (ret = lept_parse_string_raw(c, &s, &len))){ lept_set_string(v, s, len); } return ret; }

了解上面的内容后,我们可以开始下面的内容,解析对象函数的编写。

注意,我们在解析数组的时候,是把当前元素以lept_value压入栈中,而在这里,我们则是以lept_member压入:

static int lept_parse_object(lept_context* c, lept_value* v) { size_t size,i; int ret; lept_member m; EXPECT(c, '{'); lept_parse_whitespace(c); if (*c->json == '}'){ c->json++; v->type = LEPT_OBJECT; v->u.o.m = NULL; v->u.o.size = 0; return LEPT_PARSE_OK; } m.k = NULL; size = 0; for (;;){ char* str; lept_init(&m.v); /*解析 键*/ if (*c->json != '"') { //不是字符串 ret = LEPT_PARSE_MISS_KEY; break; } if ((ret = lept_parse_string_raw(c, &str, &m.klen)) != LEPT_PARSE_OK){ //解析字符串错误 break; } //否则解析字符串正确,将字符串保存在m.k里面 memcpy(m.k = (char*)malloc(m.klen + 1), str, m.klen); m.k[m.klen] = '\0'; //解析冒号 lept_parse_whitespace(c); if (*c->json != ':') { //不是冒号 ret = LEPT_PARSE_MISS_COLON; break; } c->json++; lept_parse_whitespace(c); //解析 值, 值是任意的JSON值,把结果写进m.v里面 if ((ret = lept_parse_value(c, &m.v)) != LEPT_PARSE_OK){ //解析错误 break; } //解析成功,把m里面的结果写入c里面 memcpy(lept_context_push(c, sizeof(lept_member)), &m, sizeof(lept_member)); size++; m.k = NULL;//如果之前缺乏冒号,或是这里解析值失败,在函数返回前我们要释放m.k。如果我们成功地解析整个成员,那么就要把 m.k 设为空指针,其意义是说明该键的字符串的拥有权已转移至栈,之后如遇到错误,我们不会重覆释放栈里成员的键和这个临时成员的键。 //解析逗号 lept_parse_whitespace(c); if (*c->json == ',') { c->json++; lept_parse_whitespace(c); } else if (*c->json == '}') { //size_t s = sizeof(lept_member)* size; c->json++; v->type = LEPT_OBJECT; v->u.o.size = size; size *= sizeof(lept_member); memcpy(v->u.o.m = (lept_member*)malloc(size), lept_context_pop(c, size), size); return LEPT_PARSE_OK; } else { ret = LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET; break; } } //for循环遇到错误时:释放临时key字符串及栈上的成员 free(m.k); for (i = 0; i < size; i++){ lept_member* m = (lept_member*)lept_context_pop(c, sizeof(lept_member)); free(m->k); lept_free(&m->v); } v->type = LEPT_NULL; return ret; } //不要忘记修改lept_parse_value函数 static int lept_parse_value(lept_context* c, lept_value* v) { /* ....*/ case '{': return lept_parse_object(c, v); }
最新回复(0)