cJSON源码解析
程序结构
这是一个十分轻量级的C语言代码结构,只有一个头文件cJSON.h,一个源文件cJSON.c和一个测试文件。它的作用是将无序的,杂乱的字符码解析成有顺序、有格式的代码行文件(JSON格式),便于查看。
解析顺序
- 测试文件 ;
- 定义文件;
- 头文件;
测试文件
看主程序
int main (int argc, const char * argv[]) {
/* a bunch of json: */
char text1[]="{\n\"name\": \"Jack (\\\"Bee\\\") Nimble\", \n\"format\": {\"type\": \"rect\", \n\"width\": 1920, \n\"height\": 1080, \n\"interlace\": false,\"frame rate\": 24\n}\n}";
char text2[]="[\"Sunday\", \"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\", \"Saturday\"]";
char text3[]="[\n [0, -1, 0],\n [1, 0, 0],\n [0, 0, 1]\n ]\n";
char text4[]="{\n \"Image\": {\n \"Width\": 800,\n \"Height\": 600,\n \"Title\": \"View from 15th Floor\",\n \"Thumbnail\": {\n \"Url\": \"http:/*www.example.com/image/481989943\",\n \"Height\": 125,\n \"Width\": \"100\"\n },\n \"IDs\": [116, 943, 234, 38793]\n }\n }";
char text5[]="[\n {\n \"precision\": \"zip\",\n \"Latitude\": 37.7668,\n \"Longitude\": -122.3959,\n \"Address\": \"\",\n \"City\": \"SAN FRANCISCO\",\n \"State\": \"CA\",\n \"Zip\": \"94107\",\n \"Country\": \"US\"\n },\n {\n \"precision\": \"zip\",\n \"Latitude\": 37.371991,\n \"Longitude\": -122.026020,\n \"Address\": \"\",\n \"City\": \"SUNNYVALE\",\n \"State\": \"CA\",\n \"Zip\": \"94085\",\n \"Country\": \"US\"\n }\n ]";
char text6[]="[\n int main() \n { \n int a=2;\n int b =3;\n int c=a+b;\n } ]";
/* Process each json textblock by parsing, then rebuilding: */
doit(text1);
doit(text2);
doit(text3);
doit(text4);
doit(text5);
doit(text6);
/* Now some samplecode for building objects concisely: */
create_objects();
return 0;
}
主程序声明了6个含有不同内容的字符数组,该程序的测试目的就是将这6组字符乱码(直接printf肯定是乱码),解析成代码行格式的文件。
可以直接看出来,doit(textn)便是解析函数。
查看测试程序中的doit函数,如下
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);
}
}
可以看出,doit所需的参数即使在主程序中声明的text字符数组。
doit中分别创建了一个输出指针*out,一个自定义类型对象cJSON json;而json中存放的内容就是解析后的文本格式数据。(见头文件解释)。cJSON_Parse(text)解析完毕的内容存入json中(见定义文件解释)
定义文件cJSON.c
cJSON *cJSON_Parse(const char *value) {return cJSON_ParseWithOpts(value,0,0);}
cJSON_Print(json)函数只有这些,显然这个实例的特点就是运用了大量的指针,内存管理和函数嵌套等方法,这样的好处是能缩减代码量级,业务逻辑也更加清楚。我们再去看cJSON_ParseWithOpts(value,0,0)对应的内容
cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated)
{
const char *end=0;
cJSON *c=cJSON_New_Item();//new 一个cJSON对象*c
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;
}
头文件cJSON.h
自定义类型cJSON一定是放在头文件中
typedef struct cJSON {
struct cJSON *next,*prev; /* next/prev两个结构体指针可以在数组/对象链表中移动,可以使用GetArraySize/GetArrayItem/GetObjectItem等方法;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;
总之,cJSON声明了类型多变,存储内容也不固定的结构体,这便于存放解析出来的多种类型数据。