文章目錄
-
-
-
- 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);
}