源码地址:https://sourceforge.net/projects/cjson/
目录
6.0 应用buffer的机制 printbuffer *p
6.0.1 ensure(printbuffer *p, int needed)
6.0.2 cJSON_strdup(const char* str)
6.4 print_array(cJSON *item,int depth,int fmt,printbuffer *p)
0.前言
先修知识:Cpp or C语言。(博主本科学过Cpp,近期完成《Essential C++》阅读)
简介:本项目名为cJSON,其中json是一种数据交换格式,开头的c表示此项目是由c语言进行编写
项目的核心目标:基于 符合json语法规则的字符串 或者 一系列以C编写的json构造语句,将 json的各元素以结构体表示 并 构造多个cJSON结构体之间的联系,为了实现可视化,对cJSON的打印也是其重要内容
使用方法:对照IDE上的代码,按照本博文章节顺序看代码
收获:
- 内存的分配与释放
- 结构体的定义与使用
- 标准化数据包的解析方法(json)
- 使用指针对情况进行判断并操作的算法架构(str指向输入,str2指向输出,根据str内的格式控制字符将其内容以一定格式复制到str2)
- 初探buffer,将数据存在buffer内,并预留空间
p.s.此项目为博主接触的第一个C/Cpp项目,因此以大致弄清核心代码为目标进行学习和整理,因为没有对C进行深入学习,势必会有许多细节被忽略,如读者发现有错误或者令你拍案叫绝的精心编码设计,还请不吝赐教(>▽<)
在此感谢友人lx的帮助与心得交流,让我能在一个相对短的时间内完成对此项目的学习
1.cJSON简介
对详细的内容有兴趣请右转其他博客,若想尽快获取足以支撑本项目的基本cjson概念可阅读此节
json是一种将多种元素组织起来的格式,其中主要考虑的元素之间的关系有两种:
- 并列关系,用 [ ] 表示,多个并列的元素在方括号内用 逗号 连接。用 [ ] 表示的内容称作数组。数组内的元素和与数组之间的关系为 child
例子: - 键-值 关系,用 { } 表示,键与值的对应关系在花括号中用 冒号 表示。用 { } 表示的内容称作对象。
例子:
其中所谓元素,可以是 字符串(用 “ ” 括起来)、数字(包括整数、浮点数)、也可以是其他 数组 或者 对象(也就是说允许嵌套)。
2.项目的文件结构及CJSON结构体定义
2.1 文件结构
以推荐的查看/学习顺序列出
- cJSON.h:cJSON结构体的定义[重要]、cJSON_Hooks结构体的定义、cJSON Types定义及一堆函数签名
- test.c:可以看作main函数,在这里给出了项目的测试案例,调用了大量 cJSON.c 的内容,经由此做入口可窥见整个项目的内容
- cJSON.c:项目的核心,重点学习对象
- README:内容不少,相当于官方的指导手册,可惜是用英文写的,个人没有使用
- LICENSE:完全没啥用
2.2 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;
struct cJSON定义如上,其成员列表说明:
- cJSON 指针 next,prev:数组中多个并列的元素之间、对象中多个并列的 键-值对之间 用 next 和 prev 链接
- cJSON 指针 child:
- 对象是一个元素,对象内部的单个 键-值对 是单个元素,对象到对象内部的第一个 键-值对,由child链接
- 数组整体是一个元素,由数组到数组内部的首个元素,由child链接
p.s.从上述定义可以感受到,数组内部的成员,各自为一个独立的JSON;一个 键-值对 整体,为一个独立的JSON。
- type:表示json元素内容的类型,其取值范围为宏定义
#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
- valuestring:仅用于json元素的内容为字符数组的情况,用此成员记录
- valueint:仅用于json元素的内容为数字的情况,该内容的int形式用此成员记录
- valuedouble:仅用于json元素的内容为数字的情况,该内容的int形式用此成员记录
- string:仅用于json元素为 键-值对 的情况,其键值(键值必为字符数组)用string表示。(也就是说其内容由valuestring / valueint+ valuedouble 表示)
模型示例:
【对象】
直接打印:
CJSON架构体的数据结构:
为突出结构的一致性,将没有用到的成员也列出来了,没有使用的变量成员为空,没有使用的指针成员用 / 标记
【数组】
直接打印:
CJSON架构体的数据结构:
以上两个图均用作示例,现在给出的目的在于理解CJSON元素及CJSON架构体的含义,具体构建方式可通过查看3.3 节所述的代码得到。
3.了解项目功能(test.c的学习)
这里我们首先从main函数出发,了解代码的功能,然后深入一层学习 test.c 前面定义的函数,在这些函数中会对cJSON.c的函数进行调用,用到时我们再深入学习cJSON.c
首先明确 main函数中并非是一个完整的内容,而是按顺序写了三个独立且完整的内容
解析(parse):这里的解析是指传入一个字符串(当然该字符串符合cjson的格式),然后将该字符串中的元素提取出来以cJSON的结构体进行表示,并根据字符串中的格式控制字符( [ ] 、{ }、: 、, )将cJSON结构体关联起来。
json架构体:解析一个完整的 符合json格式的字符串 所得到的内容称之为json架构体,具体实现中,json架构体由多个json struct 构成,每个json struct表示一个元素,不同的 json struct 通过 cJSON类 中的 next prev child 等关联起来。若将元素视为节点,将元素之间的关联视为连接线,那么一个完整的json可看作一个图的结构,因此我将其称之为json架构体。顺带一提,这个名词是博主自作主张的命名。
p.s. 需要注意的是,在test.c中的流程是:字符串 - 解析得到cJSON - 打印cJSON ,而打印cJSON和打印字符串本身貌似没啥区别,但是实际上在具体应用中我们的使用场景为:
- 解析字符串得到cJSON,即:字符串 - 解析得到cJSON
- 打印cJSON,即:cJSON - 打印cJSON
因此在 test.c 中看它的流程可能有种脱了裤子放p的感觉
内容1:定义了五个字符数组并通过 do_it 函数对该数组进行了解析和打印
/* 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 [32, -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 ]";
/* Process each json textblock by parsing, then rebuilding: */
doit(text1);
doit(text2);
doit(text3);
doit(text4);
doit(text5);
内容2:通过 dofile函数 对 tests文件夹下的 test1~5 文件内容,并在dofile中调用 do_it 对文件内容进行了解析和打印
// 这里的地址需要自己注意一下,他是以test.exr而非test.c作为当前文件进行相对寻址的
// 我这里使用的是cLION作为IDE,test.exe存放在项目目录下的cmake-build-debug文件夹下
dofile("../tests/test1");
dofile("../tests/test2");
dofile("../tests/test3");
dofile("../tests/test4");
dofile("../tests/test5");
内容3:该函数内分别对 多个cjson元素 而非整体进行了定义,通过一系列c语句对cjson结构进行了定义
create_objects();
3.1 doit函数
void doit(char *text)
{
char *out;cJSON *json;
json=cJSON_Parse(text); // 通过char* 构造json
if (!json) {printf("Error before: [%s]\n",cJSON_GetErrorPtr());} // 若构造失败
else
{
out=cJSON_Print(json); // 将json转化成可打印的字符串
cJSON_Delete(json); // 释放json所占用的空间
printf("%s\n", out); // 打印
free(out); // 释放字符串所占用的空间
}
}
如上所示此函数接受一个字符串数组 text,通过 cJSON_Parse函数 构造 json架构体,在构造成功的前提下将该架构体转换成字符串并打印,并且通过 cJSON_Delete 和 free 函数释放架构体和字符串所占用的空间。
3.2 dofile函数
/* Read a file, parse, render back, etc. */
void dofile(char *filename) // 从文件中读取json的字符串(未解析)
{
FILE *f;long len;char *data;
f=fopen(filename,"rb");
fseek(f,0,SEEK_END); // 将f偏移到文件末尾
len=ftell(f); // 返回f的当前文件位置(即文件大小)
fseek(f,0,SEEK_SET); // 将f偏移到文件开头
data=(char*)malloc(len+1);fread(data,1,len,f);fclose(f); // 分配内存空间
doit(data); // 解析字符串
free(data); // 释放空间
}
从文件中读取字符串内容并通过doit函数解析,与内容1相比只是字符串的来源不同,无根本差别
3.3 create_objects函数
此函数比较长,但是其并非一个整体流程而是将多个完整的 json 构建过程写在了一起,主要可分为以下几个部分:
【3.3.1 变量初始化/声明部分】
一些工具性质的指针、用来生成json元素的素材变量(字符串数组、int矩阵、int数组等)
cJSON *root,*fmt,*img,*thm,*fld; // 声明了一堆cJSON指针
char *out;int i; /* declare a few. */
/* Our "days of the week" array: */
const char *strings[7]={"Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"};
/* Our matrix: */
int numbers[3][3]={
{0,-1,0},{1,0,0},{0,0,1}};
/* Our "gallery" item: */
int ids[4]={116,943,234,38793};
/* Our array of "records": */
struct record f