项目为什么使用cJSON?
在一个偶然的开源项目中,该项目主要目的是通过ESP8266从网页上获取相关数据信息。当仔细地看完这个开源项目时,此时脑海中为什么使用cJSON这种轻量级的数据交换格式?我觉得该项目中使用cJSON的原因有如下:1.易于人阅读和编码,同时也易于机器的解析和生成。2.对于IOT设备来说,进行一次的数据交换必然不需要一次传输大量地数据,如果一次传输大量的数据对于微处理器的处理也是有要求地;3.网页端往往使用JavaScript Object 编写。接下来通过本篇文章详细地学习在嵌入式如何使用cJSON用于数据的传输。
cJSON数据格式使用详解
cJSON简述
cJSON是以标准C编码的一种轻量级的数据交换格式。同时也是一个超轻巧,携带方便,单文件,简单的JSON解析器。cJSON开源项目位置点击打开链接,更加详细的解释和示例请查看https://www.json.org/json-en.html主页。cJSON目前来说,就只有两个文件,cJSON.c和cJSON.h文件。直接把这里个文件添加到项目中。
cJSON结构体
typedef struct cJSON
{
struct cJSON *next, *prev;
struct cJSON *child;
int type;
char *valuestring;
int valueint;
double valuedouble;
char *string;
}cJSON;
结构体几点说明:
- cJSON结构体为一个双向链表,并可通过child指针访问下一层。
- type变量决定数据项类型(键的类型),数据项可以是字符串可以是整型,也可以是浮点型。如果是整型值得话可从valueint,如果是浮点型的话可从vauluedouble取出,依次类推。
- string可理解为节点的名称,综合此处的第2点可理解为“键”的名称。
- 如果是对象或者数组,采用的是双向链表来实现,链表中的每一个节点表示数组中的一个元素或者对象中的一个字段。其中child表示头结点,next、prev分别表示下一个节点和前一个节点。valuestring、valueint、valuedouble分别表示字符串、整数、浮点数的字面量。
cJSON的三种语法
键/值对 key:value,用半角冒号分割。比如 "name":"Faye"
文档对象JSON对象写在花括号中,可以包含多个键/值对。比如{ "name":"Faye" ,"address":"北京" }。
数组JSON数组在方括号中书写:数组成员可以是对象,值,也可以是数组(只要有意)。 {"love": ["乒乓球","高尔夫","斯诺克","羽毛球","LOL","撩妹"]}
cJSON各个API的使用
-
#define cJSON_False (1 << 0)
#define cJSON_True (1 << 1)
#define cJSON_NULL (1 << 2)
#define cJSON_Number (1 << 3)
#define cJSON_String (1 << 4)
#define cJSON_Array (1 << 5)
#define cJSON_Object (1 << 6)
#define cJSON_IsReference 256
#define cJSON_StringIsConst 512
这些宏定义是对结构体type的值定义,处理时只需要将type的值&255进行位运算,即可得到json里储存的数据类型。
-
* The cJSON structure: */
typedef struct cJSON {
struct cJSON *next,*prev; //同一级元素使用链表存储
struct cJSON *child; //如果是一个object或array的话,child为第一个儿子的指针
int type; //value的类型
char *valuestring; //如果这个value是字符串类型,则此处为字符串值
int valueint; //如果是数字的话,整数值
double valuedouble; //如果是数字的话,浮点数值
char *string; //json对象的名称
} cJSON;
-
cJSON内存管理:
hook管理函数:
在 c 语言中内存一般是 malloc 和 free 的。
为了方便用户自由的管理内存, cjson 使用 Hook 技术来让使用者可以自定义内存管理函数。
即用户自定义 malloc 和 free.
具体实现方式可以参考下面的代码, 默认使用系统的 malloc 和 free 函数, 用过 cJSON_InitHooks 函数可以替换成用户自定义的 malloc 和 free 函数。
typedef struct cJSON_Hooks {
void *(*malloc_fn)(size_t sz);
void (*free_fn)(void *ptr);
} cJSON_Hooks;
/* 对cJSON提供的分配,重分配,释放内存初始化函数 */
-
各种API调用
/* 将普通的json字符串处理成json对象,注意:使用完后需要将json指针释放*/
extern cJSON *cJSON_Parse(const char *value);
/*将cjson格式的数据,转换为普通字符串的新式,虽然json格式的数据也是一个字符串的样子,但这时候还是无法当成普通的字符串使用,注意:使用完后需要将json指针释放*/
extern char *cJSON_Print(cJSON *item);
/*将cjson格式的数据,以没有格式的形式转换成普通的字符串:也就是字符串中间不会有"\n" "\t"之类的东西存在,注意:使用完后需要将json指针释放*/
extern char *cJSON_PrintUnformatted(cJSON *item);
/*使用缓冲策略将cJSON实体呈现为文本。预缓冲是对最终大小的猜测。猜测良好可以减少重新分配。fmt=0给出未格式化,=1给出格式化*/
extern char *cJSON_PrintBuffered(cJSON *item,int prebuffer,int fmt);
/*释放内存*/
extern void cJSON_Delete(cJSON *c);
/*获取数组里面元素的个数,指针array是一个指向数组的对象*/
extern int cJSON_GetArraySize(cJSON *array);
/*获取数组里面的元素,这个元素也可能是对象,item是对应元素的下标*/
extern cJSON *cJSON_GetArrayItem(cJSON *array,int item);
/*获取键值内容(对象里面的对象),用一个新的 json 指针,指向该对象*/
extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);
/*判断是否有key键值是string的项,如果有返回1,否则返回0*/
extern int cJSON_HasObjectItem(cJSON *object,const char *string);
/*当使用cJSON_Prase()函数解析数据时,如果失败就可以调用该函数,该函数会返回错误的原因*/
extern const char *cJSON_GetErrorPtr(void);
/* 这些是构造json的一些API */
extern cJSON *cJSON_CreateNull(void); //创建一个空对象,暂时保留以后可能要用到
extern cJSON *cJSON_CreateTrue(void); //创建一个true的对象
extern cJSON *cJSON_CreateFalse(void); //创建一个false的对象
extern cJSON *cJSON_CreateBool(int b); //创建一个bool对象
extern cJSON *cJSON_CreateNumber(double num); //创建一个数字类型的对象
extern cJSON *cJSON_CreateString(const char *string); //创建一个字符串类型的对象
extern cJSON *cJSON_CreateArray(void); //创建一个数组类型的对象
extern cJSON *cJSON_CreateObject(void); //创建一个根对象,它是cjson格式的头结点
/* 创建数组其中里面可以设定为不同的数据类型*/
extern cJSON *cJSON_CreateIntArray(const int *numbers,int count); //整形
extern cJSON *cJSON_CreateFloatArray(const float *numbers,int count); //浮点型
extern cJSON *cJSON_CreateDoubleArray(const double *numbers,int count); //double型
extern cJSON *cJSON_CreateStringArray(const char **strings,int count); //字符串类型
/* 向数组中添加对象*/
extern void cJSON_AddItemToArray(cJSON *array, cJSON *item);
/*向对象中添加键值对,值的类型与相关函数有关*/
extern void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item);
/*当字符串肯定是const(即一个文字,或者与常量一样好),并且肯定能在cJSON对象中存活下来时,可以使用这个*/
extern void cJSON_AddItemToObjectCS(cJSON *object,const char *string,cJSON *item);
/* 将对项的引用附加到指定的数组/对象。当您想要将现有的cJSON添加到新的cJSON中,但又不想破坏现有的cJSON时,请使用此选项 */
extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item);
extern void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item);
/* 从数组/对象中删除/分离项 */
extern cJSON *cJSON_DetachItemFromArray(cJSON *array,int which);
extern void cJSON_DeleteItemFromArray(cJSON *array,int which);
extern cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string);
extern void cJSON_DeleteItemFromObject(cJSON *object,const char *string);
/* 更新数组项 */
extern void cJSON_InsertItemInArray(cJSON *array,int which,cJSON *newitem);//右移
extern void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem);
extern void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem);
/* 复制一个cJSON项 */
extern cJSON *cJSON_Duplicate(cJSON *item,int recurse);
/* Duplicate将在新的内存中创建与传递的cJSON相同的新项需要释放。递归!=0,它将复制与项连接的任何子节点。item->next和->prev指针从Duplicate返回时总是为0。*/
/* ParseWithOpts允许您要求(并检查)JSON终止为空,并检索到解析的最后一个字节的指针 */
/* 如果在return_parse_end中提供ptr,并且解析失败,那么return_parse_end将包含一个指向错误的指针。如果没有,那么cJSON_GetErrorPtr()就可以了。 */
extern cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated);
extern void cJSON_Minify(char *json);
/* 用于快速创建内容的宏. */
#define cJSON_AddNullToObject(object,name)
#define cJSON_AddTrueToObject(object,name)
#define cJSON_AddFalseToObject(object,name)
#define cJSON_AddBoolToObject(object,name,b)
#define cJSON_AddNumberToObject(object,name,n)
#define cJSON_AddStringToObject(object,name,s)
使用例子
先写分支在综合,先写子类在写父类
{
"application": "sqlite-server",
"port": "1",
"destination": "sqlite-client",
"payload":{
"code" : 1002,
"id": "010101010101010",
"ep":ll,
"pid":4246,
"did":528,
"serial:" 12004,
"control:"{
"on":"false",
"cmd":"inser into message value ('good today XXX!')",
"bri":100,
"sat":100,
"hue":100,
"ctp":100
}
}
}
cJSON *root = cJSON_CreateObject();
if(!root) return -1;
cJSON_AddStringToObject(root, "application", application);
cJSON_AddStringToObject(root, "port", gateway_port_string);
cJSON_AddStringToObject(root, "destination", destiantion);
cJSON *payload = cJSON_CreateObject();
if(!payload) return -1;
cJSON_AddItemToObject(root, "payload", payload);
cJSON_AddNumberToObject(payload , "code", 1002);
cJSON_AddStringToObject(payload , "id", "0101010101010");
cJSON_AddNumberToObject(payload , "ep", 11);
cJSON_AddNumberToObject(payload , "pid", 4923);
cJSON_AddNumberToObject(payload , "did", 527);
cJSON_AddNumberToObject(payload , "serial"12004);
cJSON *control = cJSON_CreateObject();
if(!control) return -1;
cJSON_AddItemToObject(payload, "control", control);
cJSON_AddStringToObject(control , "on", "false");
cJSON_AddStringToObject(control, "cmd", "inser into message values ('good today xxx!')");
cJSON_AddNumberToObject(control, "bri", 100);
cJSON_AddNumberToObject(control, "sat", 100);
cJSON_AddNumberToObject(control, "hue", 100);
cJSON_AddNumberToObject(control, "ctp", 100);