天天看點

開源項目cJSON具體實作6(對象的解析)

文章目錄

        • JSON 對象文法
        • 頭檔案
        • 測試代碼
        • 函數實作

JSON 對象文法

本章實作的是JSON對象。JSON對象的實作和JSON數組的實作很是相似,我們可以對比着來看。

JSON數組 JSON對象
開源項目cJSON具體實作6(對象的解析)
開源項目cJSON具體實作6(對象的解析)
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);
}
           

繼續閱讀