2. 类型分辨
2. 类型分辨
章节目录
解析的第一步就是分类型, JSON的类型总共有六个, 我们来看看如何分辨这六种类型.
读取传入的JSON字符串, 跳过前置的空格字符, 开始读取第一个非空格字符.
-
数值: 第一个非空字符是'0' ~ '9'或者是'-'负号, 代表这个JSON数据的类型是数值.
JSON的标准规定不支持使用0x或者0来表示十六进制或者八进制, 但是可以使用'e'或者'E'来进行科学计数法, 但是这对我们通过上面的标准来判断JSON数据类型没有影响.
-
字符串: 第一个非空字符是以引号'"'字符开始的, 代表这个JSON数据的类型是字符串.
-
布尔值: 第一个非空字符是以字母't'或者'f'开头的, 代表这个JSON数据的类型是布尔值true或者false
-
数组: 第一个非空字符是以左中括号 '[' 开头的, 代表这个JSON数据的类型是复合类型数组.
-
对象: 第一个非空字符是以左花括号 '{' 开头的, 代表这个JSON数据的类型是复合类型对象.
-
空值: 第一个非空字符是以字母'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
解析函数.