Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Latest commit

 

History

History
History
128 lines (99 loc) · 4.23 KB

File metadata and controls

128 lines (99 loc) · 4.23 KB
Copy raw file
Download raw file
Outline
Edit and raw actions

从零开始的 JSON 库教程(一):启程解答篇

  • Milo Yip
  • 2016/9/17

本文是《从零开始的 JSON 库教程》的第一个单元解答篇。解答代码位于 json-tutorial/tutorial01_answer

1. 修正 LEPT_PARSE_ROOT_NOT_SINGULAR

单元测试失败的是这一行:

EXPECT_EQ_INT(LEPT_PARSE_ROOT_NOT_SINGULAR, lept_parse(&v, "null x"));

我们从 JSON 语法发现,JSON 文本应该有 3 部分:

JSON-text = ws value ws

但原来的 lept_parse() 只处理了前两部分。我们只需要加入第三部分,解析空白,然后检查 JSON 文本是否完结:

int lept_parse(lept_value* v, const char* json) {
    lept_context c;
    int ret;
    assert(v != NULL);
    c.json = json;
    v->type = LEPT_NULL;
    lept_parse_whitespace(&c);
    if ((ret = lept_parse_value(&c, v)) == LEPT_PARSE_OK) {
        lept_parse_whitespace(&c);
        if (*c.json != '\0')
            ret = LEPT_PARSE_ROOT_NOT_SINGULAR;
    }
    return ret;
}

有一些 JSON 解析器完整解析一个值之后就会顺利返回,这是不符合标准的。但有时候也有另一种需求,文本中含多个 JSON 或其他文本串接在一起,希望当完整解析一个值之后就停下来。因此,有一些 JSON 解析器会提供这种选项,例如 RapidJSON 的 kParseStopWhenDoneFlag

2. true/false 单元测试

此问题很简单,只需参考 test_parse_null() 加入两个测试函数:

static void test_parse_true() {
    lept_value v;
    v.type = LEPT_FALSE;
    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, "true"));
    EXPECT_EQ_INT(LEPT_TRUE, lept_get_type(&v));
}

static void test_parse_false() {
    lept_value v;
    v.type = LEPT_TRUE;
    EXPECT_EQ_INT(LEPT_PARSE_OK, lept_parse(&v, "false"));
    EXPECT_EQ_INT(LEPT_FALSE, lept_get_type(&v));
}

static void test_parse() {
    test_parse_null();
    test_parse_true();
    test_parse_false();
    test_parse_expect_value();
    test_parse_invalid_value();
    test_parse_root_not_singular();
}

但要记得在上一级的测试函数 test_parse() 调用这函数,否则会不起作用。还好如果我们记得用 static 修饰这两个函数,编译器会发出告警:

test.c:30:13: warning: unused function 'test_parse_true' [-Wunused-function]
static void test_parse_true() {
            ^

因为 static 函数的意思是指,该函数只作用于编译单元中,那么没有被调用时,编译器是能发现的。

3. true/false 解析

这部分很简单,只要参考 lept_parse_null(),再写两个函数,然后在 lept_parse_value 按首字符分派。

static int lept_parse_true(lept_context* c, lept_value* v) {
    EXPECT(c, 't');
    if (c->json[0] != 'r' || c->json[1] != 'u' || c->json[2] != 'e')
        return LEPT_PARSE_INVALID_VALUE;
    c->json += 3;
    v->type = LEPT_TRUE;
    return LEPT_PARSE_OK;
}

static int lept_parse_false(lept_context* c, lept_value* v) {
    EXPECT(c, 'f');
    if (c->json[0] != 'a' || c->json[1] != 'l' || c->json[2] != 's' || c->json[3] != 'e')
        return LEPT_PARSE_INVALID_VALUE;
    c->json += 4;
    v->type = LEPT_FALSE;
    return LEPT_PARSE_OK;
}

static int lept_parse_value(lept_context* c, lept_value* v) {
    switch (*c->json) {
        case 't':  return lept_parse_true(c, v);
        case 'f':  return lept_parse_false(c, v);
        case 'n':  return lept_parse_null(c, v);
        case '\0': return LEPT_PARSE_EXPECT_VALUE;
        default:   return LEPT_PARSE_INVALID_VALUE;
    }
}

其实这 3 种类型都是解析字面量,可以使用单一个函数实现,例如用这种方式调用:

        case 'n': return lept_parse_literal(c, v, "null", LEPT_NULL);

这样可以减少一些重复代码,不过可能有少许额外性能开销。

4. 总结

如果你能完成这个练习,恭喜你!我想你通过亲自动手,会对教程里所说的有更深入的理解。如果你遇到问题,有不理解的地方,或是有建议,都欢迎在评论或 issue 中提出,让所有人一起讨论。

下一单元是和数字类型相关,敬请期待。

Morty Proxy This is a proxified and sanitized view of the page, visit original site.