cjson学习篇三:cJSON数据结构和设计思想
说明:
本文章旨在总结备份、方便以后查询,由于是个人总结,如有不对,欢迎指正;另外,内容大部分来自网络、书籍、和各类手册,如若侵权请告知,马上删帖致歉。
QQ 群 号:513683159 【相互学习】
内容来源:
CSDN-cJSON使用详细教程 | 一个轻量级C语言JSON解析器
JSON的简单介绍以及C语言的JSON库使用
JSON的简单介绍&cJSON库使用(一)
cJSON数据结构
typedef struct cJSON
{
struct cJSON *next; //遍历数组或对象链的后向链表指针
struct cJSON *prev; //遍历数组或对象链的前向链表指针
struct cJSON *child; //数组或对象链的孩子节点
int type; //key的类型
char *valuestring; //字符串值
int valueint; //整数值
double valuedouble; //浮点数值
char *string; //key的名字
} cJSON;
从以上代码可看出,cJSON设计思想是:链表。
数据指向:
next
指向下一节点,prev
指向上一节点,child
指向子节点(故:对象/数组是可以嵌套,指向新链表)
数据存储:
string
表示key的名称,type
表示key的类型,valuestring、valueint、valuedouble
则表示对应type
类型的变量。
PS:
一个结点表示 数组的一个元素/对象的一个字段。
cJSON 类型
不管是数值类型、字符串类型或者对象类型等都使用该结构体,类型信息通过标识符 type来进行判断,cJSON总共定义了7种类型:
#define cJSON_Invalid (0) //表示一个无效的不包含任何值的项。如果将项设置为所有零字节,则自动具有此类型。
#define cJSON_False (1 << 0) //表示一个错误的布尔值。您还可以使用cJSON_IsBool检查布尔值。
#define cJSON_True (1 << 1) //表示一个正确的布尔值。您还可以使用cJSON_IsBool检查布尔值。
#define cJSON_NULL (1 << 2) //表示一个空值。
#define cJSON_Number (1 << 3) //表示一个数字值。该值在valuedouble和valueint中以double形式存储。如果数值超出了整数的范围,则valueint使用INT_MAX或INT_MIN。
#define cJSON_String (1 << 4) //表示一个字符串值。它以零终止字符串的形式存储在valuestring中。
#define cJSON_Array (1 << 5) //表示一个数组值。这是通过将child指向表示数组中值的cJSON项链表来实现的。元素使用next和prev链接在一起,其中第一个元素是prev。next == NULL和最后一个元素next == NULL。
#define cJSON_Object (1 << 6) //表示一个对象值。对象的存储方式与数组相同,唯一的区别是对象中的项将它们的键存储为string。
#define cJSON_Raw (1 << 7) //表示任何类型的JSON,它存储在valuestring中以零结尾的字符数组中。例如,可以使用它来避免反复打印相同的静态JSON以节省性能。cJSON在解析时永远不会创建这种类型。还要注意,cJSON不检查它是否是有效的JSON。
另外还有以下两个标志:
cJSON_IsReference:指定子元素所指向的项和/或valuestring不属于这个项,它只是一个引用。所以cJSON_Delete和其他函数将只释放这个项,而不是它的子元素/valuestring。
cJSON_StringIsConst:这意味着字符串指向常量字符串。这意味着cJSON_Delete和其他函数不会尝试释放字符串。
一、构建cJSON
对于每个值类型,都有一个cJSON_Create xxx
可用于创建该类型项的函数。所有这些都将分配一个cJSON
结构,稍后可以用cJSON_Delete()
删除它。请注意,您必须在某些时候删除它们,否则将导致内存泄漏。
重要提示:
如果你已经添加了一个项到一个数组或一个对象,你不能删除它与cJSON_Delete
。将它添加到数组或对象中会转移它的所有权,这样当数组或对象被删除时,它也会被删除。您还可以使用cJSON_SetValuestring()
来更改cJSON_String()
的valuestring
,而不需要手动释放前面的valuestring
。
(1)创建:
①创建任何类型的JSON (cJSON_CreateRaw)
CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw);
②创建对象的JSON
CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); //创建一个空对象
1.创建:
cJSON_CreateObject()
创建一个空对象。
cJSON_CreateObjectReference()
创建一个不“拥有”其内容的对象,因此它的内容不会被cJSON_Delete删除。
2.添加项:
cJSON_AddItemToObject()
向对象添加项.
cJSON_AddItemToObjectCS()
向名称为常量或引用(项目的key
,cJSON
结构中的字符串)的对象添加一个项目,这样它就不会被cJSON_Delete()
释放。
cJSON_AddItemReferenceToArray()
,可添加一个元素作为对另一个对象、数组或字符串的引用。这意味着cJSON_Delete()
不会删除该项目的子属性或valuestring
属性,因此如果它们已经在其他地方使用,则不会发生双重释放。
3.取出项:
cJSON_DetachItemFromObjectCaseSensitive()
从一个对象中取出一个项,会返回分离的项,所以一定要把它分配给一个指针,否则你会有一个内存泄漏。
4.删除项:
cJSON_DeleteItemFromObjectCaseSensitive()
删除项目.工作方式类似于cJSON_DetachItemFromObjectCaseSensitive()
,然后是cJSON_Delete()
。
cJSON_ReplaceItemInObjectCaseSensitive()
或cJSON_ReplaceItemViaPointer()
在适当的位置替换对象中的项.若失败,cJSON_ReplaceItemViaPointer()
将返回0。这在内部所做的是分离旧的项,删除它并在其位置插入新项。
5.获取大小:
cJSON_GetArraySize()
获得对象的大小.(这样可以工作,因为在内部对象被存储为数组。)
6.访问项:
cJSON_GetObjectItemCaseSensitive()
访问一个对象中的一个项
7.遍历:
要遍历一个对象,可以使用cJSON_ArrayForEach
宏,方法与使用数组相同。
8.其他:
cJSON
还提供了方便的帮助函数,可以快速创建新项并将其添加到对象中,比如cJSON_AddNullToObject()
。它们返回一个指向新项的指针,如果失败则返回NULL
。
③创建数组的JSON
CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void);
1.创建:
cJSON_CreateArray()
创建一个空数组。
cJSON_CreateArrayReference()
创建一个不“拥有”其内容的数组,所以它的内容不会被cJSON_Delete()
删除。
2.添加项:
cJSON_AddItemToArray()
将项添加到数组中,将项追加到末尾。
cJSON_AddItemReferenceToArray()
可将一个元素添加为另一个项、数组或字符串的引用。这意味着cJSON_Delete
将不会删除那些项的子属性或valuestring
属性,因此,如果它们已经在其他地方使用了,就不会发生重复释放。
cJSON_InsertItemInArray()
,中间插入项,它将在给定的基于0的索引处插入一个项,并将所有现有项向右移动。
3.取出项:
cJSON_DetachItemFromArray()
,从一个给定索引的数组中取出一个项目并继续使用它,它将返回分离的项目,所以一定要将它分配给一个指针,否则您将有内存泄漏。
4.删除项:
cJSON_DeleteItemFromArray()
删除项目。它的工作原理类似于cJSON_DetachItemFromArray()
,但是通过cJSON_Delete
删除分离的项目。
5.替换项:
cJSON_ReplaceItemInArray()
用索引/cJSON_ReplaceItemViaPointer()
给定元素指针,在适当的位置替换数组中的项.若cJSON_ReplaceItemViaPointer()
失败,它将返回0。这在内部做的是分离旧项、删除它并在其位置插入新项。
6.获取大小:
cJSON_GetArraySize()
获得数组的大小。使用cJSON_GetArrayItem()
获取给定索引处的元素。
7.遍历:
因为数组存储为一个链表,通过迭代索引效率低下(O (n²)),所以你可以使用cJSON_ArrayForEach
宏遍历一个数组在O (n)时间复杂度。
④创建基础类型的JSON
CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void);
CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean);
CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num);
CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string);
(2)删除
释放cJSON
CJSON_PUBLIC(void) cJSON_Delete(cJSON *item); //将JSON结构所占用的数据空间释放
二、解析cJSON
①解析cJSON
CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);
CJSON_PUBLIC(cJSON *) cJSON_ParseWithLength(const char *value, size_t buffer_length);
cJSON_Parse()
:对给定以零结尾的字符串中的一些JSON进行解析。
cJSON_ParseWithLength()
:给定一个字符串中的一些JSON(无论是否终止为0)进行解析。
1.空间分配:
将解析JSON并分配一个表示它的cJSON项树。一旦它返回,将完全负责在与cJSON_Delete()
一起使用后对它进行释放。分配器默认是malloc
和free
,但是可以使用cJSON_InitHooks()
(全局)更改。
2.发生错误:
cJSON_GetErrorPtr()
:访问指向输入字符串中错误位置的指针。
注意:这可能会在多线程场景中产生竞争条件,在这种情况下,最好使用cJSON_ParseWithOpts()
和return_parse_end
。默认情况下,解析后的JSON之后的输入字符串中的字符不会被视为错误。
3.更多选项
若想要更多的选项,使用cJSON_ParseWithOpts()
。return_parse_end
返回一个指针,指向输入字符串中的JSON结尾或错误发生的位置(从而以线程安全的方式替换cJSON_GetErrorPtr()
。require_null_ended
,如果设置为1,那么如果输入字符串包含JSON
之后的数据,则会导致错误。
若你想要更多的设置缓冲区长度的选项,可以使用cJSON_ParseWithLengthOpts()
②输出JSON
CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item);
cJSON_Print
()给定一个cJSON
项树,将它们打印为字符串(将使用空白来打印格式)
它将分配一个字符串并将树的JSON
表示形式打印到其中。一旦它返回,您就完全有责任在与分配器一起使用后重新分配它。(通常是免费的,取决于cJSON_InitHooks
设置了什么)。
cJSON_PrintUnformatted ()
打印没有格式,则可使用该函数。
cJSON_PrintBuffered ()
:若对结果字符串的大小有一个大致的概念,则可使用该函数。
cJSON_PrintPreallocated()
避免这些动态缓冲区分配。它接受一个缓冲区的指针打印到它的长度。如果达到该长度,打印将失败并返回0。如果成功,则返回1。注意,您应该提供比实际需要更多的5个字节,因为cJSON在估计所提供的内存是否足够时不是100%准确的。
③其他
1.从object中获取item “string”(根据键找json结点)
CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
2.判断是否有key是string的项 如果有返回1 否则返回0
CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);
3 . 返回数组(或对象)中的项数
CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array);
4.从数组“array”中检索项目编号“index”(根据数组下标index取array数组结点的第index个成员 返回该成员节点)
CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
5.遍历数组
#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next)
实验:
实验一:构建json,并将其保存到文件中。
创建文件:creat_json.c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "cJSON.h"
#define CFGFILE "creat_json.json"
//构建以下json
// {
// "name":"王小二", //字符串
// "age":18, //数值
// "favorite_food":["dumplings","ribs"], //数组
// "has_girlfriend":false, //布尔值
// "car":null, //null
// "address": //对象
// {
// "country":"China",
// "city":"fuzhou",
// "postcode":350000
// }
// }
char *buf = NULL;
void test()
{
cJSON *object = cJSON_CreateObject(); //创建一个空对象
cJSON *name = cJSON_CreateString("王小二"); //创建基础类:string,并添加到对象中
cJSON_AddItemToObject(object, "name", name);
cJSON *age = cJSON_CreateNumber(18); //创建基础类:number,并添加到对象中
cJSON_AddItemToObject(object, "age", age);
cJSON *array = cJSON_CreateArray(); //创建数组:array ,并添加到对象中
cJSON_AddItemToObject(object, "favorite_food", array);
cJSON *dumplings = cJSON_CreateString("dumplings"); //创建基础类:string ,并添加到数组中
cJSON_AddItemToArray(array, dumplings);
cJSON *ribs = cJSON_CreateString("ribs"); //创建基础类:string ,并添加到数组中
cJSON_AddItemToArray(array, ribs);
cJSON *has_girlfriend = cJSON_CreateFalse(); //创建基础类:false,并添加到对象中
cJSON_AddItemToObject(object, "has_girlfriend", has_girlfriend);
cJSON *car = cJSON_CreateNull(); //创建基础类:null,并添加到对象中
cJSON_AddItemToObject(object, "car", car);
cJSON *address =cJSON_CreateObject();
cJSON_AddItemToObject(object, "address", address);
cJSON *country = cJSON_CreateString("China"); //创建基础类:string,并添加到对象中
cJSON_AddItemToObject(address, "country", country);
cJSON *city = cJSON_CreateString("fuzhou"); //创建基础类:string,并添加到对象中
cJSON_AddItemToObject(address, "city", city);
cJSON *postcode = cJSON_CreateNumber(350000); //创建基础类:string,并添加到对象中
cJSON_AddItemToObject(address, "postcode", postcode);
//将内容存到CFGFILE文件中
FILE *fp = fopen(CFGFILE,"w");
buf = cJSON_Print(object);
fwrite(buf,strlen(buf),1,fp);
fclose(fp);
cJSON_Delete(object);
}
int main()
{
test();
printf("%s\n",buf); //显示屏上查看对应的数据
}
执行指令:
gcc creat_json.c -o creat -lcjson
或gcc creat_json.c cJSON.c -I ./ -o creat
实验二:解析文件
文件1:conf_s_1.json
{
"name": "T1","type": "s1"
}
文件2:conf_s_2.json
{
"T": [
{"name": "T1","type": "s1"},
{"name": "T2","type": "s2"}
]
}
#include <stdlib.h>
#include <stdio.h>
#include "cJSON.h"
#define CFGFILE1 "conf_s_1.json"
#define CFGFILE2 "conf_s_2.json"
void test1(){
//将文本内容存到字符串data中
//打开文件
FILE* file = fopen(CFGFILE1,"rb");
//获得文件长度
fseek(file,0,SEEK_END);
long len=ftell(file);
fseek(file,0,SEEK_SET);
//开辟内存,将文件内容读取到data中
char* data = (char*)malloc(len+1);
fread(data,1,len,file);
//关闭文件
fclose(file);
cJSON* root = cJSON_Parse(data); //将cJSON实体呈现为用于传输/存储的文本
cJSON* name_json = cJSON_GetObjectItem(root, "name"); //从对象中获取项目“string”。不区分大小写
char* name = cJSON_Print(name_json); //将cJSON实体呈现为用于传输/存储的文本,而不进行任何格式化
printf("name:%s\n", name);
free(name);
cJSON* type_json = cJSON_GetObjectItem(root, "type"); //从对象中获取项目“string”。不区分大小写
char* type = cJSON_Print(type_json); //将cJSON实体呈现为用于传输/存储的文本,而不进行任何格式化
printf("type:%s\n", type);
free(type);
cJSON_Delete(root); //删除一个cJSON实体和所有子实体
free(data); //删除内存
}
void test2(){
//将文本内容存到字符串data中
//打开文件
FILE* file = fopen(CFGFILE2,"rb");
//获得文件长度
fseek(file,0,SEEK_END);
long len=ftell(file);
fseek(file,0,SEEK_SET);
//开辟内存,将文件内容读取到data中
char* data = (char*)malloc(len+1);
fread(data,1,len,file);
//关闭文件
fclose(file);
cJSON* root = cJSON_Parse(data);
cJSON* OBJroot=cJSON_GetObjectItem(root, "T");
int array_size = cJSON_GetArraySize(OBJroot);
for (int i = 0; i < array_size; i++) {
printf("i:%d\n", i);
cJSON *object = cJSON_GetArrayItem(OBJroot, i);
cJSON *name_json = cJSON_GetObjectItem(object, "name");
char *name = cJSON_Print(name_json); //将JSON结构体打印到字符串中 需要自己释放
printf("name:%s\n", name);
free(name);
cJSON *type_json = cJSON_GetObjectItem(object, "type");
char *type = cJSON_Print(type_json); //将JSON结构体打印到字符串中 需要自己释放
printf("type:%s\n", type);
free(type);
}
cJSON_Delete(root);
free(data);
}
int main()
{
test1();
//test2();
}
实验三:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include"cJSON.h"
int main(void)
{
char *string = "{\"family\":[\"father\",\"mother\",\"brother\",\"sister\",\"somebody\"]}";
//从缓冲区中解析出JSON结构
cJSON *json = cJSON_Parse(string);
cJSON *node = NULL;
//cJOSN_GetObjectItem 根据key来查找json节点 若果有返回非空
node = cJSON_GetObjectItem(json,"family");
if(node == NULL)
{
printf("family node == NULL\n");
}
else
{
printf("found family node\n");
}
node = cJSON_GetObjectItem(json,"famil");
if(node == NULL)
{
printf("famil node == NULL\n");
}
else
{
printf("found famil node\n");
}
//判断是否有key是string的项 如果有返回1 否则返回0
if(1 == cJSON_HasObjectItem(json,"family"))
{
printf("found family node\n");
}
else
{
printf("not found family node\n");
}
if(1 == cJSON_HasObjectItem(json,"famil"))
{
printf("found famil node\n");
}
else
{
printf("not found famil node\n");
}
node = cJSON_GetObjectItem(json,"family");
if(node->type == cJSON_Array)
{
printf("array size is %d",cJSON_GetArraySize(node));
}
//非array类型的node 被当做array获取size的大小是未定义的行为 不要使用
cJSON *tnode = NULL;
int size = cJSON_GetArraySize(node);
int i;
for(i=0;i<size;i++)
{
tnode = cJSON_GetArrayItem(node,i);
if(tnode->type == cJSON_String)
{
printf("value[%d]:%s\n",i,tnode->valuestring);
}
else
{
printf("node' type is not string\n");
}
}
cJSON_ArrayForEach(tnode,node)
{
if(tnode->type == cJSON_String)
{
printf("int forEach: vale:%s\n",tnode->valuestring);
}
else
{
printf("node's type is not string\n");
}
}
return 0;
}
更多推荐
所有评论(0)