2. 类型分辨

2. 类型分辨

章节目录

1. 解析总体流程

2. 类型分辨

3. 数值解析

4. 字符串解析

5. 数组解析

6. 对象解析

解析的第一步就是分类型, JSON的类型总共有六个, 我们来看看如何分辨这六种类型.

读取传入的JSON字符串, 跳过前置的空格字符, 开始读取第一个非空格字符.

  1. 数值: 第一个非空字符是'0' ~ '9'或者是'-'负号, 代表这个JSON数据的类型是数值.

    JSON的标准规定不支持使用0x或者0来表示十六进制或者八进制, 但是可以使用'e'或者'E'来进行科学计数法, 但是这对我们通过上面的标准来判断JSON数据类型没有影响.

  2. 字符串: 第一个非空字符是以引号'"'字符开始的, 代表这个JSON数据的类型是字符串.

  3. 布尔值: 第一个非空字符是以字母't'或者'f'开头的, 代表这个JSON数据的类型是布尔值true或者false

  4. 数组: 第一个非空字符是以左中括号 '[' 开头的, 代表这个JSON数据的类型是复合类型数组.

  5. 对象: 第一个非空字符是以左花括号 '{' 开头的, 代表这个JSON数据的类型是复合类型对象.

  6. 空值: 第一个非空字符是以字母'n' 开头的, 代表这个JSON数据的类型是空值null.

现在我们有了判断JSON数据的标准, 来试着写一下这个代码

int __parse_json_value(const char *jsonStr, json_value_t *value)
{
    int ret = -1;

    if (jsonStr == NULL || value == NULL)
    {
        return ret;
    }

    while (isspace(*jsonStr))
    {
        jsonStr++;
    }

    switch (*jsonStr)
    {
    case '-':
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
    {
        ret = __parse_json_number(jsonStr, value);
        if (ret < 0)
        {
            return -2;
        }

        value->type = JSON_VALUE_NUMBER;
        break;
    }
    case '\"':
    {
        ret = __parse_json_string(jsonStr, value);
        if (ret < 0)
        {
            return -2;
        }

        value->type = JSON_VALUE_STRING;
        break;
    }
    case 't':
    {
        if (strncmp(jsonStr, "true", 4) != 0)
            return -2;

        value->type = JSON_VALUE_TRUE;
        jsonStr += 4;
        break;
    }
    case 'f':
    {
        if (strncmp(jsonStr, "false", 5) != 0)
            return -2;

        value->type = JSON_VALUE_FALSE;
        jsonStr += 5;
        break;
    }
    case '[':
    {
        ret = __parse_json_array(jsonStr, value);
        if (ret < 0)
        {
            return -2;
        }

        value->type = JSON_VALUE_ARRAY;
        break;
    }
    case '{':
    {
        ret = __parse_json_object(jsonStr, value);
        if (ret < 0)
        {
            return -2;
        }

        value->type = JSON_VALUE_OBJECT;
        break;
    }
    case 'n':
    {
        if (strncmp(jsonStr, "null", 4) != 0)
            return -2;

        value->type = JSON_VALUE_NULL;
        jsonStr += 4;
        break;
    }
    default:
        return -2;
    }

    return 0;
}

这个代码很简单, 函数进来先进行了简单的参数判断, 然后跳过了开始的空格字符, 开始进行switch中的类型判断.

在switch的类型判断中, 实现了上面分析的功能, 对应的__parse_json_XXX​的函数后面进行实现, 这些函数代表着对应类型的解析处理.

这个代码有需要改进的地方, 对于入参jsonStr​这个是外部传入的指针, 内部会对这个字符串进行单字符的遍历处理, 在遇到字符串结束符'\0'​或者非法字符结束转换, 结束转换的时候不一定遍历到了原字符串末尾, 外部可能需要继续处理这个字符串, 所以我们需要将处理过后的, 到无法处理的最后一位字符的指针end​返回给外部, 交由外部处理, 就像接龙一样, 告诉外部, 传入的字符串, 经过我们的处理处理到了end​这个地方, 后面的我们没法处理了, 外部来继续处理或者判断错误吧. 所以我们需要加入一个参数, const char **end​用于传递这个end​指针, 这也是和C语言标准库中的字符串处理函数保持了一致.

而指针jsonStr​一直处理的就是当前需要解析的字符, 故将其改名为cursor​, 像一个游标一样, 这样, 我们可以把代码改成下面这个样子.

static int __parse_json_value(const char *cursor, const char **end, json_value_t *val)
{
    int ret = -1;

    switch (*cursor)
    {
    case '-':
    case '0':
    case '1':
    case '2':
    case '3':
    case '4':
    case '5':
    case '6':
    case '7':
    case '8':
    case '9':
    {
        ret = __parse_json_number(cursor, end, &val->value.number);
        if (ret < 0)
        {
            return ret;
        }

        val->type = JSON_VALUE_NUMBER;
        break;
    }
    case '\"':
    {
        cursor++;
        ret = __parse_json_string(cursor, end, &val->value.string);
        if (ret < 0)
        {
            return ret;
        }

        val->type = JSON_VALUE_STRING;
        break;
    }
    case 't':
    {
        if (strncmp(cursor, "true", 4) != 0)
        {
            return -2;
        }

        *end = cursor + 4;
        val->type = JSON_VALUE_TRUE;

        break;
    }
    case 'f':
    {
        if (strncmp(cursor, "false", 5) != 0)
        {
            return -2;
        }

        *end = cursor + 5;
        val->type = JSON_VALUE_FALSE;
        break;
    }
    case '[':
    {
        ret = __parse_json_array(cursor, end, &val->value.array);
        if (ret < 0)
        {
            return ret;
        }

        val->type = JSON_VALUE_ARRAY;
        break;
    }
    case '{':
    {
        ret = __parse_json_object(cursor, val);
        if (ret < 0)
        {
            return ret;
        }

        val->type = JSON_VALUE_OBJECT;
        break;
    }
    case 'n':
    {
        if (strncmp(cursor, "null", 4) != 0)
        {
            return -2;
        }

        *end = cursor + 4;
        val->type = JSON_VALUE_NULL;
        break;
    }
    default:
        return -2;        
    }

    return 0;
}

这里将这个函数定义为了static​, 因为这个函数只会在内部调用, 调用权在内部, 所以参数校验也取消了.

另外将__parse_json_XXX​函数的参数直接修改成了对应的类型.

通过这个函数, 已经实现了JSON数据的类型分辨, 同时对于简单的类型, 布尔值(true和false)以及空值(null)直接进行了转换, 剩余的, 需要实现对应类型的__parse_json_XXX​解析函数.

参考

[1] Workflow 源码解析 Json parser :part1 parse - 知乎 (zhihu.com)

posted @ 2023-08-08 11:41  明天咪西什么  阅读(6)  评论(0编辑  收藏  举报