Json-Tutorial01 特殊字面量解析——false/true/null

1. 简介

本栏目的内容来自于一个轻量级的json解析库设计教程:https://github.com/miloyip/json-tutorial,主要根据其中的tutorial来介绍设计的逻辑。下面来看一个简单的json文本:

{
    "title": "Design Patterns",
    "subtitle": "Elements of Reusable Object-Oriented Software",
    "author": [
        "Erich Gamma",
        "Richard Helm",
        "Ralph Johnson",
        "John Vlissides"
    ],
    "year": 2009,
    "weight": 1.8,
    "hardcover": true,
    "publisher": {
        "Company": "Pearson Education",
        "Country": "India"
    },
    "website": null
}

从此例子可看出,JSON 是树状结构,而 JSON 只包含 6 种数据类型:

  • null: 表示为 null
  • boolean: 表示为 true 或 false
  • number: 一般的浮点数表示方式,在下一单元详细说明
  • string: 表示为 "..."
  • array: 表示为 [ ... ]
  • object: 表示为

我们要实现的 JSON 库,主要是完成 3 个需求:

  1. 把 JSON 文本解析为一个树状数据结构(parse)。
  2. 提供接口访问该数据结构(access)。
  3. 把数据结构转换成 JSON 文本(stringify)

2. 字面量解析

本节主要完成三个字面量false/true/null的解析,比较简单。主要的解析函数是lept_parse_value。这个函数会检测json字符数组的第一个字符,如果为n就按照null解析,f和t以此类推。

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

这里lept_cntext的结构其实就是要解析的字符数组:

typedef struct {
    const char* json;
}lept_context;

lept_value是解析出来的结果,即我们常说的Json Value。lept_type是一个枚举类。这里lept_value为啥只有一个type呢,因为我们第一节只解析三个字面量,true/false/null,他们是没有值的,只用类型就可以表示。

typedef struct {
    lept_type type;
}lept_value;

typedef enum { LEPT_NULL, LEPT_FALSE, LEPT_TRUE, LEPT_NUMBER, LEPT_STRING, LEPT_ARRAY, LEPT_OBJECT } lept_type;

2.1 lept_parse_xxx

解析true/false/null这三个字面量的逻辑差不多,以null为例,我们写一个函数lept_parse_null。

static int lept_parse_null(lept_context* c, lept_value* v) {
    EXPECT(c, 'n');
    if (c->json[0] != 'u' || c->json[1] != 'l' || c->json[2] != 'l')
        return LEPT_PARSE_INVALID_VALUE;
    c->json += 3;
    v->type = LEPT_NULL;
    return LEPT_PARSE_OK;
}

解析null时,从第二个字母开始判断,依次为u、l、l则解析成功。如果有一个字母不满足要求,则直接return LEPT_PARSE_INVALID_VALUE,短路后面的判断。如果往后的三个字母都满足要求(ull),则将字符数粗指针往后移动3。v->type设置为null,解析成功。

2.2 多root的情况

如果仅仅是这么修改,是无法通过全部的test用例的。来看一个例子:

static void test_parse_root_not_singular() {
    lept_value v;
    v.type = LEPT_FALSE;
    EXPECT_EQ_INT(LEPT_PARSE_ROOT_NOT_SINGULAR, lept_parse(&v, "null x"));
    EXPECT_EQ_INT(LEPT_NULL, lept_get_type(&v));
}

这里解析的json字符串是null x。如果按照之前lept_parse_null的逻辑,解析完null之后就算是成功了。实际上null后面还跟着一个空格和x,这是不合法的。所以我们在解析完null之后,还得看下空格后面有没有字符。

int lept_parse(lept_value* v, const char* json) {
    /*........*/
    lept_parse_whitespace(&c); /*去除前置空格*/
    if ((ret = lept_parse_value(&c, v)) == LEPT_PARSE_OK) {
        /*对字面量解析完成后,跳过后面的空格*/
        lept_parse_whitespace(&c);
        /*r如果空格后面的第一个字符不是结束符,则说明有多个root,解析失败*/
        if (*(c.json) != '\0') {
            return LEPT_PARSE_ROOT_NOT_SINGULAR;
        }
    }
    return ret;
}

这样,就可以通过所有的用例了。

posted @ 2022-12-29 20:38  南小小小小乔  阅读(118)  评论(0)    收藏  举报