目录
1、通过README文件,初步了解cJSON:
1.1、头文件的开头和结尾:
1.2、头文件关于cJSON类型的宏定义
1.3、头文件中的extern
2、阅读并且分析cJSON源码
2.1、结构体struct cJSON(算法设计思想):
2.2、结构体stuct cJSON_Hooks:
2.3、cJSON_InitHooks函数
2.4、cJSON_New_Item函数
2.5、cJSON_Delete函数
2.6、parse_number函数
2.6、pow2gt函数
2.8、update函数
2.9、print_number函数
2.1.1、parse_hex4函数
2.1.2、parse_string函数
2.1.3、print_string_ptr函数
2.1.4、cJSON_ParseWithOpts函数
2.1.5、parse_array函数
2.1.6、parse_object函数
2.1.7、print_object函数
2.1.8、print_array函数
3、cJSON实例
3.1、源码包中test.c测试代码
3.2、用例
从README文件内容的介绍来看,cJSON是一个用C语言编写的JSON数据解析器,它轻便,可移植,是一个简单易用的解析器。它cJson的源码文件只有两个:一个C 文件(cJSON.c)和一个头文件(cJSON.h)。因为它的简单易用,让其GitHub吸引了全球众多的程序员在它的基础上贡献代码。可以使用git clone https://github.com/DaveGamble/cJSON.git语句拷贝到本地,也可以到cJSON download | SourceForge.net网址上下载到本地。
#ifndef cJSON_h,#define cJSON_h,#endif . 这是为了防止头文件被重复引用。
extern "C"的主要作用就是为了能够正确实现C++代码调用其他C语言代码。加上extern "C"后,会指示编译器这部分代码按C语言的进行编译,而不是C++的。由于C++支持函数重载,因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中,而不仅仅是函数名;而C语言并不支持函数重载,因此编译C语言代码的函数时不会带上函数的参数类型,一般之包括函数名。
这些宏定义是对结构体type的值定义,处理时只需要将type的值&255进行位运算,即可得到json里储存的数据类型。
extern是C语言中的一个关键字,一般用在变量名前或函数名前,作用是用来说明“此变量/函数是在别处定义的,要在此处引用”
在头文件(cJSON.h)中:
cJSON用了一个结构体来表示一个JSON的数据,它并不是把一整段的JSON数据全部抽象出来,而是把其中的一条JSON数据抽象出来。
cjson的存储结构像一个广义表,其实也可以说是一个树,不过兄弟结点之间都通过prev和next两个指针连接起来。
prev和next分别是cjson对象的前驱和后继,属于同一级别的对象。chid则指向孩子结点,并且是第一个孩子的指针。
分析:
Prev指针:用于指向上一个键值对
JSON是一段数据,结构体对其中一条数据进行存储,为了能对整段数据进行遍历、查找、删除、添加,cJSON用了链表进行存储。
2、Child指针:用于指向新的链表
3、String:表示该条JSON的数据名称;
type:表示该条JSON的数据类型;
valuestring:如果type是字符串类型,则将该指针指向该条数据,表示数据的值;
valueint:如果type是整数,则将该指针指向该条数据,表示数据的值;
valuedouble:如果type是浮点数,则将该指针指向该条数据,表示数据的值;
结构体中定义了一系列的成员变量来存放值,以一种键值对的形式来存储,简洁优美。
在头文件(cJSON.h)中:
分析:定义了malloc函数以及free函数,malloc 函数其实就是在内存中:找一片指定大小的空间,然后将这个空间的首地址给一个指针变量,这里的指针变量可以是一个单独的指针,也可以是一个数组的首地址, 这要看malloc函数中参数size的具体内容。Free函数是将之前用malloc分配的空间还给程序或者是操作系统,也就是释放了这块内存,让它重新得到自由。通过结构体 struct cJSON_Hooks跟内部调用的内存分配挂钩。
在C文件(cJSON.c)中:
在cJSON项目里,留有cJSON_InitHooks()函数,他是Hooks内存管理函数。
分析:
malloc函数和free函数,则指针指向自定义的函数,否则指向默认函数。
在C文件(cJSON.c)中:
它不仅能初始化内存,还返回了一个cJSON节点。
分析:
在C文件(cJSON.c)中:
它可以用来删除一整个的JSON数据,同时将所有的节点全部释放内存。
分析:
Parse_number函数能把数字字符数组转换为基本数据类型赋值给一条cJSON数据,并且返回数字字符数组。
分析:
该函数作用是返回比x大的最小的2的N次方数。
分析:
2.7、结构体struct printbuffer与ensure函数
结构体printbuffer内定义了一系列的与内存有关的变量,ensure函数用来分配空间,确保内存够用,并且返回可用的内存指针。每次给printbuffer赋值前,都需要计算所需的内存,并调用ensure()确保内存够用。
分析:
10、最后返回新分配的可用内存的起点
更新函数,用来更新printbuffer中的偏移量offset,返回更新后的offset
分析:
打印数字函数
分析:
如果是,且p不为空,则确保p所需要的内存足够,如果p不为空,则申请一个大小为21字节的新的空间。如果str不为空,则调用sprintf函数,把格式化的数据写入字符串str。所以这里的意思是,把item->valueint数字写入str字符串。
该函数能把四位16进制的数转为十进制的数。
该函数能从字符串中解析字符串,解析的时候会把 转换成真正的回车和换行,而不是简单的字符串复制。
分析一下未满足条件的,也就是flag为1的时候。这就是,在上图的黄色框中的部分的条件成立的,即可以字符串的第一个字符是双引号或反斜杠或者ASCII码小于32的,我们先看看小于32的ASCII码包括哪些内容,如下图:
可以看到,都是一些控制字符串,非显示字符串,控制字符是用来实现特定操作的,如一些比较常用的控制字符就是ESC、BACKSPACE(删除上一个字符)、Del(删除当前字符)、回车符、换行符等。首先判断是不是空串,如果是就直接输出俩双引号。
接下来,我们来分析最后的while()部分。这部分其实也很好理解,就是是显示字符就直接保存,不是的话需要判断以及加一些反斜杠等。需要注意的是最后default语句,sprintf()函数和printf()函数是相似的,前面加个 s 表示的是string嘛,不同的是printf()是将内容写在窗口上,而sprintf()函数是将内容写到其第一个参数里面,在这里指的是ptr2.而“ux”指的是以4位16进制写入,0表示的是不足4为就补0,4表示的是4位的意思,x表示的是16进制。
Print_string函数内部调用了print_String_ptr,能针对一条具体的cJSON数据进行打印
ParseWithOpts允许JSON是空终止的,并检索到解析的最后一个字节的指针。如果在return_parse_end中提供一个ptr,但解析失败,那么return_parse_end将包含一个指向 错误的指针,因此将匹配cJSON_GetErrorPtr()。cJSON_parse函数内部调用了cJSON_ParseWithOpts函数。
该函数能将字符串解析为数组。解析数组时,其基本思想是对数组中每一个元素递归调用parse_value,再将这些元素连接形成一个链表。
该函数可以将字符串解析为对象。
该函数能用来打印JSON对象。
分析else部分,大抵意思应该挺明显的,就是把链表中的每个结点的里的元素存储在entries中。
按逻辑分析,下面这个也很好理解了,就是将所有的字符串串成一个整的字符串,包括中括号,逗号等符号。其实这样也就分析完了。在细枝末节的东西就不扣了,也都是一些比较基本的东西,包括出错的处理了等等。
该函数能用来打印数组
部分运行结果:
实例:
{
"name": "Andy", //键值对1
"age": 20 //键值对2
}
代码展示:
void Parse_Str1(void){ char str1[] = "{"name":"Andy","age":20}"; cJSON *str1_json, *str1_name, *str1_age; printf("str1:%s
",str1); str1_json = cJSON_Parse(str1); //创建JSON解析对象,返回JSON格式是否正确 if (!str1_json) { printf("JSON格式错误:%s
", cJSON_GetErrorPtr()); //输出json格式错误信息 } else { printf("JSON格式正确:
%s
",cJSON_Print(str1_json) ); str1_name = cJSON_GetObjectItem(str1_json, "name"); //获取name键对应的值的信息 if (str1_name->type == cJSON_String) { printf("姓名:%s
", str1_name->valuestring); } str1_age = cJSON_GetObjectItem(str1_json, "age"); //获取age键对应的值的信息 if(str1_age->type==cJSON_Number) { printf("年龄:%d
", str1_age->valueint); } cJSON_Delete(str1_json);//释放内存 }}
运行结果:
参考:https://blog.csdn.net/qq_38289815/article/details/103307262