標籤:

從零開始的 JSON 庫教程(一):啟程解答篇

本文是《從零開始的 JSON 庫教程》第一個單元的解答篇。解答代碼位於 json-tutorial/tutorial01_answer。

(題圖 Photo by Greg Rakozy)

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 != ) 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 : 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 中提出,讓所有人一起討論。

下一單元是和數字類型相關,敬請期待。

推薦閱讀:

JSON适合大数据传输吗?跨语言JSON数据传输需要注意什么?
從零開始的 JSON 庫教程(六):解析對象
How write comment in JSON(package.json)
「每日一題」JSON 是什麼?
乾貨 | 劫持各個瀏覽器中的JSON漏洞

TAG:JSON | CC | 教程 |