cjson源代码解读 (二)解析流程
先从test.c 开始说, 我们看到test.c 里面有两个函数用来测试, doit 和dofile, 一个是读char* 一个是读file, 肯定是读字符串的要简单, 所以先看doit.
/* Parse text to JSON, then render back to text, and print! */ void doit(char *text) { char *out;cJSON *json; json=cJSON_Parse(text); if (!json) {printf("Error before: [%s]\n",cJSON_GetErrorPtr());} else { out=cJSON_Print(json); cJSON_Delete(json); printf("%s\n",out); free(out); } }
先是申请了一个cJSON型的指针, 这个指针应该就是主要的存储结构了. 那这个指针是什么构成的呢?
typedef struct cJSON { struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ int type; /* The type of the item, as above. */ char *valuestring; /* The item's string, if type==cJSON_String */ int valueint; /* The item's number, if type==cJSON_Number */ double valuedouble; /* The item's number, if type==cJSON_Number */ char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ } cJSON;
1) next和prev是两个指向自己下一个和前一个的指针, 如果next==0 表明是末尾, prev==0表明是开头.
2) string是自己的值, 也就是 {"name":"v1"} 中的name
3) type是当前节点value的类型, 也就是json默认定义的几种.
4) 根据type的种类, 分别有对应的值 valuestring, valueint, valuedouble和 child. true和false还有null当然不需要定义, 因为他们本来值就是自己.
type定义在 <cJSON.h> 开头
/* cJSON Types: */ #define cJSON_False 0 #define cJSON_True 1 #define cJSON_NULL 2 #define cJSON_Number 3 #define cJSON_String 4 #define cJSON_Array 5 #define cJSON_Object 6
然后就是核心parse的过程了, 也就是函数 json=cJSON_Parse(text); 代码如下:
/* Default options for cJSON_Parse */ cJSON *cJSON_Parse(const char *value) {return cJSON_ParseWithOpts(value,0,0);}
竟然就这么一行, 这不是糊弄我们吗, 那就接着看cJSON_PaarseWithOpts函数.
定义:
/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ extern cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated);
实现:
/* Parse an object - create a new root, and populate. */ cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated) { const char *end=0; cJSON *c=cJSON_New_Item(); ep=0; if (!c) return 0; /* memory fail */ end=parse_value(c,skip(value)); if (!end) {cJSON_Delete(c);return 0;} /* parse failure. ep is set. */ /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ if (require_null_terminated) {end=skip(end);if (*end) {cJSON_Delete(c);ep=end;return 0;}} if (return_parse_end) *return_parse_end=end; return c; }
不管后面两个参数, 后面两个参数都是0. (应该在子函数里面有用)
先大致明确一下流程, 然后再看函数怎么实现的(有一些是错误输出, 暂不看):
1. cJSON *c = cJSON_New_Item(); 申请一个cJSON对象出来, 也就相当于new一个.
2. end=parse_value(c,skip(value)); 我们注意, 看doit代码的时候只有一句parse, 也就是真正的parse在这个地方(脑补一下).
3. 剩下的都是错误处理.
那么怎么申请对象呢, 看cJSON_New_Item()函数.
/* Internal constructor. */ static cJSON *cJSON_New_Item(void) { cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON)); if (node) memset(node,0,sizeof(cJSON)); return node; }
static void *(*cJSON_malloc)(size_t sz) = malloc;
申请一个sizeof(cJSON)结构体大小的空间. 然后将内容初始化.
下面看解析函数
/* Utility to jump whitespace and cr/lf */ static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;}
这个非常好理解, 去除最前面的空格等字符.
/* Parser core - when encountering text, process appropriately. */ static const char *parse_value(cJSON *item,const char *value) { if (!value) return 0; /* Fail on null. */ if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; } if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; } if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; } if (*value=='\"') { return parse_string(item,value); } if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); } if (*value=='[') { return parse_array(item,value); } if (*value=='{') { return parse_object(item,value); } ep=value;return 0; /* failure. */ }
1. 如果啥都没有, 直接返回了.
2. 然后是只有一个null或false或true的情况.
3. 然后是转义字符.
4. 然后是数字
5. 然后是数组或者字典.
看到这里也能想明白了, value就是指json的单个value, 所以这个函数应该不止会调用一次的. 接下来就是数字, 字符串, 数组, 字典的解析.
这个会在后面讲.