cJSON的下载和安装
- 源码下载:
- 地址一:git clone https://github.com/DaveGamble/cJSON.git
//安装和编译
mkdir build
cd build
cmake ..
make
sudo make install
- 地址二:https://sourceforge.net/projects/cjson/
- 解压 unzip XXX.zip
- 将
cJson.c
,cJSON.h
拷贝到自定义的目录 - 编译:
gcc cJSON.c test.c -o test -lm
(添加数学库) - 基于JSON相关的c函数库,生成json文件
cJSON函数分析
cJSON
函数库的核心结构体就是一个cJSON
cJSON
是使用链表来存储数据的,其访问方式像一颗树。每一个节点可以有兄弟节点,通过next/prev
指针来查找,它类似双向链表;每个节点也可以有孩子节点,通过child
指针来访问,进入下一层。只有节点是对象或数组时才可以有孩子节点。type
是键(key)
的类型,一共有7种取值,分别是:False
,Ture
,NULL
,Number
,String
,Array
,Object
。若是Number
类型,则valueint
或valuedouble
中存储值。若期望的是int
,则访问valueint
;若期望的是double
,则访问valuedouble
,可以得到值。若是String
类型的,则valuestring
中存储值,可以访问valuestring
得到值。string
中存放的是这个节点的名字,可理解为key
的名称。
#define cJSON_Invalid (0)
#define cJSON_False (1 << 0)//1
#define cJSON_True (1 << 1)//2
#define cJSON_NULL (1 << 2)//4
#define cJSON_Number (1 << 3)//8
#define cJSON_String (1 << 4)//16
#define cJSON_Array (1 << 5)//32
#define cJSON_Object (1 << 6)//64
#define cJSON_Raw (1 << 7)//128 /* raw json */
#define cJSON_IsReference 256
#define cJSON_StringIsConst 512
typedef struct cJSON {
struct cJSON *next,*prev; /* 遍历数组或对象链的前向或后向链表指针*/
struct cJSON *child; /* 数组或对象的孩子节点*/
int type; /* key的类型*/
char *valuestring; /* 字符串值*/
int valueint; /* 整数值*/
double valuedouble; /* 浮点数值*/
char *string; /* key的名字*/
} cJSON;
JSON的三种语法
- 键/值对
key:value
,用半角冒号分割。比如"name":"Faye"
。 - 文档对象
JSON
对象写在花括号中,可以包含多个键/值对。比如{"name":"Faye", "address":"北京"}
。 - 数组
JSON
数组在方括号中书写:数组成员可以是对象、值,也可以是数组(只要有意义)。比如{"love": ["乒乓球","高尔夫","斯诺克","羽毛球","LOL","撩妹"]}
。
JSON的两种结构
- 对象:表示为“{}”括起来的内容,数据结构为
{key:value,key:value,...}
的键值对的结构,在面向对象的语言中,key
为对象的属性,value
为对应的属性值,所以很容易理解,取值方法为对象.key
获取属性值; - 数组:表示为中括号“[]”括起来的内容,数据结构为
["java","javascript","vb",...]
,取值方式和所有语言中一样,使用索引获取。
写JSON文件的函数
//将传入的JSON结构转化为字符串,将cJSON数据解析成JSON字符串,并在堆中开辟一块char*的内存空间存储JSON字符串。
/* Render a cJSON item/entity/structure to text. */
char *cJSON_Print(cJSON *item) {return print_value(item,0,1,0);}
char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0,0);}
//创建一个值类型的数据
cJSON *cJSON_CreateNumber(double num);
cJSON *cJSON_CreateString(const char *string);
cJSON *cJSON_CreateArray(void);
//创建一个对象(文档)
cJSON *cJSON_CreateObject(void);
//将创建的cJSON数组/对象添加到链表中
cJSON *cJSON_CreateIntArray(const int *numbers,int count);
/* Append item to the specified array/object. */
void cJSON_AddItemToArray(cJSON *array, cJSON *item);
void cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);
//从现有的cJSON链表中删除一个对象
/* Remove/Detatch items from Arrays/Objects. */
CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item);
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which);
CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which);
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string);
CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string);
CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string);
CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string);
//创建并添加cJSON对象到指定的链表
/* Helper functions for creating and adding items to an object at the same time.
* They return the added item or NULL on failure. */
CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name);
CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name);
//JSON嵌套 :
【 向对象中增加键值对】 cJSON_AddItemToObject(root, “rows”, 值类型数据相关函数());
【 向对象中增加数组】 cJSON_AddItemToObject(root, “rows”, cJSON_CreateArray());
【 向数组中增加对象】 cJSON_AddItemToArray(rows, cJSON_CreateObject());
//几个能提高操作效率的宏函数 :
#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name,cJSON_CreateNumber(n))
#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s))
解析JSON格式的函数
//cJSON_Parse解析JSON数据包,并按照cJSON结构体的结构序列化整个数据包。使用该函数会通过malloc()函数在内存中开辟空间,使用完需要手动释放。cJSON *root=cJSON_Parse(json_string);
cJSON *cJSON_Parse(const char *value);
//cJSON_GetObjectItem从cJSON结构体中查找某个子节点名称(键名称),如果查找成功可把该子节点序列化到cJSON结构体中。cJSON *item=cJSON_GetObjectItem(root,"firstName");
cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);
//cJSON_Delete用于释放cJSON_Parse()分配出来的内存空间。cJSON_Delete(root);
void cJSON_Delete(cJSON *c);
//获取数组长度
int cJSON_GetArraySize(cJSON *array);
//获取数组成员,根据数组下标index取array数组结点的第index个成员 返回该成员节点
cJSON *cJSON_GetArrayItem(cJSON *array,int item); c
//根据键找json结点 :
cJSON *cJSON_GetObjectItem(cJSON *object,const char *string)
//遍历数组 :
#define cJSON_ArrayForEach(pos, head) for(pos = (head)->child; pos != NULL; pos = pos->next)
cJSON使用实例
- 实例一:简单解析JSON数据
#include <stdio.h>
#include <stdlib.h>
#include "cJSON.h"
char text[] = "{\"timestamp\":\"2020-08-10 08:45:57\", \"value\":1}";
int main(int argc, const char *argv[])
{
cJSON *json, *json_value, *json_timestamp;
json = cJSON_Parse(text);
if(NULL == json)
{
printf("Error before: [%s]\n", cJSON_GetErrorPtr());
return -1;
}
json_value = cJSON_GetObjectItem(json, "value");
if(json_value->type == cJSON_Number)
{
printf("value: %d\n", json_value->valueint);
}
json_timestamp = cJSON_GetObjectItem(json, "timestamp");
if(json_timestamp->type == cJSON_String)
{
printf("%s\n", json_timestamp->valuestring);
}
cJSON_Delete(json);
return 0;
}
输出:
value: 1
2020-08-10 08:45:57
- 实例二:按不同要求解析json的方法
#include <stdio.h>
#include <stdlib.h>
#include "cJSON.h"
void printJson(cJSON * root)//以递归的方式打印json的最内层键值对
{
int i = 0;
for(; i<cJSON_GetArraySize(root); i++) //遍历最外层json键值对
{
cJSON * item = cJSON_GetArrayItem(root, i);
if(cJSON_Object == item->type) //如果对应键的值仍为cJSON_Object就递归调用printJson
printJson(item);
else //值不为json对象就直接打印出键和值
{
printf("%s->", item->string);
printf("%s\n", cJSON_Print(item));
}
}
}
int main()
{
char * jsonStr = "{\"semantic\":{\"slots\":{\"name\":\"张三\"}}, \"rc\":0, \
\"operation\":\"CALL\", \"service\":\"telephone\", \"text\":\"打电话给张三\"}";
cJSON * root = NULL;
cJSON * item = NULL;//cjson对象
root = cJSON_Parse(jsonStr);
if (!root)
{
printf("Error before: [%s]\n",cJSON_GetErrorPtr());
}
else
{
printf("%s\n", "有格式的方式打印Json:");
printf("%s\n\n", cJSON_Print(root));
printf("%s\n", "无格式方式打印json:");
printf("%s\n\n", cJSON_PrintUnformatted(root));
printf("%s\n", "一步一步的获取name 键值对:");
printf("%s\n", "获取semantic下的cjson对象:");
item = cJSON_GetObjectItem(root, "semantic");//
printf("%s\n", cJSON_Print(item));
printf("%s\n", "获取slots下的cjson对象");
item = cJSON_GetObjectItem(item, "slots");
printf("%s\n", cJSON_Print(item));
printf("%s\n", "获取name下的cjson对象");
item = cJSON_GetObjectItem(item, "name");
printf("%s\n", cJSON_Print(item));
printf("%s:", item->string); //看一下cjson对象的结构体中这两个成员的意思
printf("%s\n", item->valuestring);
printf("\n%s\n", "打印json所有最内层键值对:");
printJson(root);
}
return 0;
}
输出:
有格式的方式打印Json:
{
"semantic": {
"slots": {
"name": "张三"
}
},
"rc": 0,
"operation": "CALL",
"service": "telephone",
"text": "打电话给张三"
}
无格式方式打印json:
{"semantic":{"slots":{"name":"张三"}},"rc":0,"operation":"CALL","service":"telephone","text":"打电话给张三"}
一步一步的获取name 键值对:
获取semantic下的cjson对象:
{
"slots": {
"name": "张三"
}
}
获取slots下的cjson对象
{
"name": "张三"
}
获取name下的cjson对象
"张三"
name:张三
打印json所有最内层键值对:
name->"张三"
rc->0
operation->"CALL"
service->"telephone"
text->"打电话给张三"
- 实例三:创建JSON数据
#include <stdio.h>
#include <stdlib.h>
#include "cJSON.h"
int main(void)
{
char *cjson_str = NULL;
cJSON * root = cJSON_CreateObject();
cJSON * item = cJSON_CreateObject();
cJSON * next = cJSON_CreateObject();
cJSON_AddItemToObject(root, "rc", cJSON_CreateNumber(0));//根节点下添加
cJSON_AddItemToObject(root, "operation", cJSON_CreateString("CALL"));
cJSON_AddItemToObject(root, "service", cJSON_CreateString("telephone"));
cJSON_AddItemToObject(root, "text", cJSON_CreateString("打电话给张三"));
cJSON_AddItemToObject(root, "semantic", item);//root节点下添加semantic节点
cJSON_AddItemToObject(item, "slots", next);//semantic节点下添加item节点
cJSON_AddItemToObject(next, "name", cJSON_CreateString("张三"));//添加name节点
//cjson_str = cJSON_Print(root);
cjson_str =cJSON_PrintUnformatted(root);
printf("first json:\n%s\n", cjson_str);
free(cjson_str);
cJSON_AddStringToObject(next, "number", "13423452334");
cJSON_AddNumberToObject(next, "age", 35);
cJSON_AddBoolToObject(next, "man", 1);
//cjson_str = cJSON_Print(root);
cjson_str =cJSON_PrintUnformatted(root);
printf("second json:\n%s\n", cjson_str);
free(cjson_str);
cJSON_Delete(root);
return 0;
}
输出:
first json:
{"rc":0,"operation":"CALL","service":"telephone","text":"打电话给张三","semantic":{"slots":{"name":"张三"}}}
second json:
{"rc":0,"operation":"CALL","service":"telephone","text":"打电话给张三","semantic":{"slots":{"name":"张三","number":"13423452334","age":35,"man":true}}}
- 实例四:创建Array的JSON数据
#include <stdio.h>
#include <stdlib.h>
#include "cJSON.h"
char *makeArray(int iSize)
{
int i;
char *out = NULL;
cJSON *root = cJSON_CreateArray();
if(NULL == root)
{
printf("create json array failed!");
return NULL;
}
for(i = 0; i < iSize; i++)
{
cJSON_AddNumberToObject(root, "hehe", i + 1);
}
out = cJSON_Print(root);
cJSON_Delete(root);
return out;
}
void parseArray(char *pJson)
{
int i = 0;
int iSize = 0;
cJSON *root = NULL;
if(NULL == pJson)
return;
root = cJSON_Parse(pJson);
if(NULL == root)
return;
iSize = cJSON_GetArraySize(root);
for(i = 0; i < iSize;i++)
{
cJSON *psub = cJSON_GetArrayItem(root, i);
if(NULL == psub)
continue;
printf("value[%2d] : %d\n", i, psub->valueint);
}
cJSON_Delete(root);
}
int main(void)
{
char *json = NULL;
json = makeArray(5);
printf("create json: %s\n", json);
parseArray(json);
free(json);
return 0;
}
输出:
create json: [1, 2, 3, 4, 5]
value[ 0] : 1
value[ 1] : 2
value[ 2] : 3
value[ 3] : 4
value[ 4] : 5
- 实例五
json数据采用链表存储
typedef struct cJSON {
struct cJSON *next,*prev; // 数组 对象数据中用到
struct cJSON *child; // 数组 和对象中指向子数组对象或值
int type; // 元素的类型,如是对象还是数组
char *valuestring; // 如果是字符串
int valueint; // 如果是数值
double valuedouble; // 如果类型是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;
如果有一个json数据
{
"name": "Jack (\"Bee\") Nimble",
"format": {
"type": "rect",
"width": 1920,
"height": 1080,
"interlace": false,
"frame rate": 24
}
}
可以进行如下操作
//将字符串解析成json结构体
cJSON *root = cJSON_Parse(my_json_string);
//获取某个元素
cJSON *format = cJSON_GetObjectItem(root,"format");
int framerate = cJSON_GetObjectItem(format,"frame rate")->valueint;
//json结构体转换成字符串
char *rendered=cJSON_Print(root);
//删除json结构体
cJSON_Delete(root);
//构建一个json结构体
cJSON *root,*fmt;
root=cJSON_CreateObject();
cJSON_AddItemToObject(root, "name", cJSON_CreateString("Jack (\"Bee\") Nimble"));
cJSON_AddItemToObject(root, "format", fmt=cJSON_CreateObject());
cJSON_AddStringToObject(fmt,"type", "rect");
cJSON_AddNumberToObject(fmt,"width", 1920);
cJSON_AddNumberToObject(fmt,"height", 1080);
cJSON_AddFalseToObject (fmt,"interlace");
cJSON_AddNumberToObject(fmt,"frame rate", 24);
- 实例六:将json数据写入文件
void WriteJson()
{
char *char_json = "{\"hello\":\"你好\"}";
//从缓冲区中解析出JSON结构
cJSON *json = cJSON_Parse(char_json);
if (json == NULL)
{
return;
}
//将传入的JSON结构转化为字符串
char *buf = NULL;
buf = cJSON_Print(json);
//打开一个info.json文件,并写入json内容
FILE *fp = fopen("info.json", "w");
fwrite(buf, strlen(buf), 1, fp);
fclose(fp);//关闭文件
free(buf);//释放资源
cJSON_Delete(json);//释放资源
}
int main()
{
WriteJson();
return 0;
}
其他参考:https://zhuanlan.zhihu.com/p/51781273
cJSON内存泄露注意事项
申请内存 | 释放内存 |
---|---|
cJSON_Parse | cJSON_Delete |
cJSON_Create | cJSON_Delete |
cJSON_Print | cJSON_free |
-
在cjson中释放内存时,只需要释放父节点就可以,不需要专门管理子节点,子节点会随着父节点的释放一并释放掉,即通过cJSON_AddItemToObject函数加入到数组或者object中,不需要单独释放new_json ,删除json对象时被添加的new_json同时也会被删除。对于子节点,最好的做法是在程序处理完成数据后,判断一下每个节点是不是NULL。
-
cjson中的string类型数据在写入时是内存拷贝,并不是内存指向,cjson中占用的内存,会在删除父节点的时候一并被释放掉。
-
字符串格式化函数调用后返回的字串内存,需要专门调用free函数释放掉