从零开始的 JSON 库教程(六):解析对象

79 阅读2分钟

代码地址:github.com/WangHao-nlp…

第六节主要负责解析对象,这里简单分析下json对象,json值,json的区别:
JSON 是 JS 对象的字符串表示法,使用文本表示一个 JS 对象的信息,本质是一个字符串。在代码中是leptjson.cpplept_contex里的const char* json;
JSON对象是用大括号括起来的,里面有多个键值对,其中键是字符串格式,值可以是空、布尔、数字、字符串、数组、和对象(对象里面可以再套对象)。键值对在代码里面是leptjson.h里的lept_member,json对象是lept_value.o,由键值对指针和size构成; JSON值是空、布尔、数字、字符串、数组、和对象,在代码里是lept_value,包括lept_type和具体内容。

leptjson.h

// lept_value与lept_member相互引用
//typedef struct lept_value lept_value;  
typedef struct lept_member lept_member;

struct lept_value {
    union {
        struct { lept_member* m; size_t size; }o;  // json对象(键值对组成) member(一组键值对,char* :json对象), size
        struct { lept_value* e; size_t size; } a;  
        struct { char* s; size_t len; } s;         
        double n;
    }u;
    lept_type type;
};

struct lept_member {
    char* k; size_t klen;
    lept_value v;
};

解析对象的逻辑与数组类似,也是嵌套递归

leptjson.cpp

static int lept_parse_object(lept_context* c, lept_value* v) {
    size_t i, size;  // 用来统计json中键值对个数,用来释放内存
    lept_member m;
    int ret;
    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;
        }
        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);
        // 解析值
        if ((ret = lept_parse_value(c, &m.v)) != LEPT_PARSE_OK) {
            break;
        }
        // 把键值对指针存进json语句的栈中
        memcpy(lept_context_push(c, sizeof(lept_member)), &m, sizeof(lept_member));
        size++;
        // 准备重新开始下一个
        m.k = NULL;
        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;
            // 把栈中内存存进json的对象指针里
            memcpy(v->u.o.m = (lept_member*)malloc(s), lept_context_pop(c, s), s);
            return LEPT_PARSE_OK;
        }
        else {
            ret = LEPT_PARSE_MISS_COMMA_OR_CURLY_BRACKET;
            break;
        }
    }
    /* Pop and free members on the stack */
    // 逐个释放json键值对
    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;
}

释放内存

void lept_free(lept_value* v) {
    size_t i;
    assert(v != NULL);
    switch (v->type) {
        case LEPT_STRING:
            free(v->u.s.s);
            break;
        case LEPT_ARRAY:
            for (i = 0; i < v->u.a.size; i++)
                lept_free(&v->u.a.e[i]);
            free(v->u.a.e);
            break;
        case LEPT_OBJECT:
            for (i = 0; i < v->u.o.size; i++) {
                free(v->u.o.m[i].k);
                lept_free(&v->u.o.m[i].v);
            }
            free(v->u.o.m);
            break;
        default: break;
    }
    v->type = LEPT_NULL;
}

test.cpp

缺少键

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,");

缺少冒号

TEST_ERROR(LEPT_PARSE_MISS_COLON, "{\"a\"}");
TEST_ERROR(LEPT_PARSE_MISS_COLON, "{\"a\",\"b\"}");

缺少大括号或者逗号

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\":{}");

解析结果

教程6.png