1.json简介
2.cjson介绍
3.cjson应用实例
3.0.实例源码下载
3.1.json标准格式
标准JSON的合法符号:{(左大括号) }(右大括号) "(双引号) :(冒号) ,(逗号) [(左中括号) ](右中括号)
JSON字符串:特殊字符可在字符前面加 \ 或使用 \u 加 4位16进制数来处理
- {"name":"jobs"}
JSON布尔:必须小写的true和false
- {"bool":true}
JSON空:必须小写的null
- {"object":null}
JSON数值:不能使用8/16进制
- {"num":60}
- {"num":-60}
- {"num":6.6666}
- {"num":1e+6}<!-- 1乘10的6次方,e不区分大小写 -->
- {"num":1e-6}<!-- 1乘10的负6次方,e不区分大小写 -->
JSON对象
- {
- "starcraft": {
- "INC": "Blizzard",
- "price": 60
- }
- }
JSON数组
- {
- "person": [
- "jobs",
- 60
- ]
- }
JSON对象数组
- {
- "array": [
- {
- "name": "jobs"
- },
- {
- "name": "bill",
- "age": 60
- },
- {
- "product": "war3",
- "type": "game",
- "popular": true,
- "price": 60
- }
- ]
- }
参考: http://www.json.org/json-zh.html
3.2.使用cJSON创建JSON字符串
在Linux下,使用C语言编程,开始JSON字符串的创建。我们还是一步步来,逐渐由简单到复制。
1,下载源码
可以从如下网站来下载:https://sourceforge.net/projects/cjson/ 。
2,包含cJSON的源码
下载下来,解压后,从里面找到两个文件(cJSON.c、cJSON.h),复制到我们的工程里面。只需在函数中包含头文件(#include “cJSON.h”),然后和cJSON.c一起编译即可使用。
3,创建一个键值对
首先是一个简单的键值对字符串,要生成的目标如下:
{"firstName":"Brett"}
要进行创建,就是先确定键与值,然后转为cJSON格式。我们很容易就能明确键为firstName,值为Brett,可是,使用cJSON怎么创建呢?
对于这个简单的例子,我们需要调用cJSON的五个接口函数就可以实现创建了。(有人不乐意了:都五个函数了,你还说“就可以了”----其实是一法通,百法通,学会了这个创建,其他的创建动作都是非常类似的。)
这五个函数的原型如下:
cJSON*cJSON_CreateObject ();
cJSON*cJSON_CreateString(const char *string);
voidcJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item);
voidcJSON_Delete(cJSON *c);
char*cJSON_Print(cJSON *item);
下面按创建过程来描述一次:
(1) 首先调用cJSON_ CreateObject ()函数,创建一个JSON对象,之后便可向这个对象中添加string或int等内容的数据项了。使用该函数会通过malloc()函数在内存中开辟一个空间,使用完成需要手动释放。
cJSON*root=cJSON_CreateObject();
(2) 调用cJSON_CreateString ()函数,由一个字符串生成一个cJSON的数据项。
cJSON*item=cJSON_CreateString("Brett");
(3) 将上一步生成的数据项与其键值("firstName")一起添加到root对象中。
cJSON_AddItemToObject(root,"firstName",item);
其实到这一步,我们在内存中的cJSON对象就已经构建完成了,后面是展示结果了。
(4) 将cJSON对象的内容解析为字符串,并展示出来。
out=cJSON_Print(root);
printf("out:%s\n",out);
(5) 通过cJSON_Delete(),释放cJSON_CreateObject ()分配出来的内存空间。
cJSON_Delete(root);
(6) 释放cJSON_Print ()分配出来的内存空间。
free(out);
这样就完成了一次cJSON接口调用,实现了字符串的创建工作。
4,转换一个结构体
接下来,我们来个复杂一点的,将一个结构体转换为JSON字符串,结构体定义如下:
typedefstruct
{
char firstName[32];
char lastName[32];
char email[64];
int age;
float height;
} PERSON;
看起来比一个键值对复杂多了,我们又需要学习新的接口函数了吗?
是的,由于出现了数字,我们需要学习一个新函数:
cJSON *cJSON_CreateNumber(double num);
当然,创建的步骤要更复杂一些,下面我仍然是按创建过程来描述一次:
(1)还是先调用cJSON_ CreateObject ()函数,创建一个JSON对象root,做为根(咱们可以把JSON串看成是一颗树)。使用该函数会通过malloc()函数在内存中开辟一个空间,使用完成需要手动释放。
cJSON*root=cJSON_CreateObject();
(2)继续调用cJSON_ CreateObject ()函数,创建一个JSON对象obj_person,做为挂载结构体内容的对象。挂载内容之后,这个对象是要挂载到根上的。
cJSON*obj_person=cJSON_CreateObject();
(3)根据数据生成cJSON格式的数据项,调用cJSON_AddItemToObject()函数挂载到obj_person对象上。这个过程,要多次重复,直到将所有数据都添加上。此时要注意,不同的成员,生成的方法是不一样的。
cJSON*item=cJSON_CreateString(person->firstName);
cJSON_AddItemToObject(obj_person,"firstName",item);
item=cJSON_CreateString(person->lastName);
cJSON_AddItemToObject(obj_person,"lastName",item);
item=cJSON_CreateString(person->email);
cJSON_AddItemToObject(obj_person,"email",item);
item=cJSON_CreateNumber(person->age);
cJSON_AddItemToObject(obj_person,"age",item);
item=cJSON_CreateNumber(person->height);
cJSON_AddItemToObject(obj_person,"height",item);
(4)将obj_person对象挂载到根上。
cJSON_AddItemToObject(root,"person",obj_person);
到这一步,我们在内存中的cJSON对象就已经构建完成了,后面就是展示结果。
(5)将cJSON对象的内容解析为字符串,并展示出来。
out=cJSON_Print(root);
printf("out:%s\n",out);
(6)通过cJSON_Delete(),释放cJSON_CreateObject ()分配出来的内存空间。这里说明一下,我们前面调用了2次cJSON_CreateObject (),最后只需要针对root调用一次释放即可,因为第二次创建的对象也是挂接在root上的。
cJSON_Delete(root);
(7)释放cJSON_Print ()分配出来的内存空间。
free(out);
至此,我们就使用cJSON接口完成了由结构体生成JSON字符串的工作。
5,创建结构体数组的JSON串
最后,我们来个更复杂一些的,来转换一个数组,并且数组的成员是结构体!我们要生成的目标如下:
{
"people":[
{"firstName":"z","lastName":"Jason","email":"bbbb@126.com","height":1.67},
{"lastName":"jadena","email":"jadena@126.com","age":8,"height":1.17},
{"email":"cccc@126.com","firstName":"z","lastName":"Juliet","age":36,"height":1.55}
]
}
此时,我们又需要学习新的接口了,一个是创建数组,一个是取数组成员,函数原型如下:
cJSON*cJSON_CreateArray(void);
void cJSON_AddItemToArray(cJSON *array, cJSON*item);
由于前面已经实现了单个结构体的转换,这里我们重点关注下数组的相关调用。
(1)还是先调用cJSON_ CreateObject ()函数,创建一个JSON对象root,做为根。
(2)调用cJSON_CreateArray ()函数,创建一个JSON数组对象,准备挂载多个结构体对象。挂载内容之后,这个数组对象是要挂载到根上的。
cJSON*array_person=cJSON_CreateArray();
(3)生成一个结构体对象,并相应添加数据,然后调用cJSON_AddItemToArray()函数挂载到数组对象上。这个过程,要多次重复,直到将所有结构体对象都添加上。
cJSON_AddItemToArray(array_person,obj_person);
(4)将数组对象挂载到根上。
cJSON_AddItemToObject(root,"people",array_person);
到这一步,我们在内存中的cJSON对象就已经构建完成了。
(5)将cJSON对象的内容解析为字符串,并展示出来。
(6)通过cJSON_Delete(),释放cJSON_CreateObject ()分配出来的内存空间。
(7)释放cJSON_Print ()分配出来的内存空间。
这样,我们就使用cJSON接口完成了将结构体数组转换成JSON字符串的工作。
详细代码见后文附带例程。
说明:
本文所附带例程,实现了结构体数组生成JSON字符串,只是一个学习之作,对于初学cJSON的同学,可以有些借鉴参考的作用。
附带例程:
- #include <stdio.h>
- #include <string.h>
- #include <sys/types.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include "cJSON.h"
- typedef struct
- {
- int id;
- char firstName[32];
- char lastName[32];
- char email[64];
- int age;
- float height;
- }people;
- void dofile(char *filename);/* Read a file, parse, render back, etc. */
- int main(int argc, char **argv)
- {
- dofile("json_str4.txt");
- return 0;
- }
- //create a key-value pair
- int str_to_cJSON(char *json_string, char *str_val)
- {
- char * out=NULL;
- cJSON *root=cJSON_CreateObject();
- if (!root)
- {
- printf("Error before: [%s]\n",cJSON_GetErrorPtr());
- return -1;
- }
- else
- {
- cJSON *item=cJSON_CreateString("Brett");
- cJSON_AddItemToObject(root,"firstName",item);
- out=cJSON_Print(root);
- printf("out2:%s\n",out);
- cJSON_Delete(root);
- if(out!=NULL)
- {
- free(out);
- }
- }
- return 0;
- }
- //create a object from struct
- int struct_to_cJSON(char *json_string, people *person)
- {
- if((json_string==NULL) || (person==NULL))
- {
- printf("%s: input is invalid",__func__);
- }
- char * out=NULL;
- cJSON *root=cJSON_CreateObject();
- if (!root)
- {
- printf("Error before: [%s]\n",cJSON_GetErrorPtr());
- return -1;
- }
- else
- {
- cJSON *obj_person=cJSON_CreateObject();
- cJSON *item=cJSON_CreateString(person->firstName);
- cJSON_AddItemToObject(obj_person,"firstName",item);
- item=cJSON_CreateString(person->lastName);
- cJSON_AddItemToObject(obj_person,"lastName",item);
- item=cJSON_CreateString(person->email);
- cJSON_AddItemToObject(obj_person,"email",item);
- item=cJSON_CreateNumber(person->age);
- cJSON_AddItemToObject(obj_person,"age",item);
- item=cJSON_CreateNumber(person->height);
- cJSON_AddItemToObject(obj_person,"height",item);
- cJSON_AddItemToObject(root,"person",obj_person);
- out=cJSON_Print(root);
- printf("out2:%s\n",out);
- cJSON_Delete(root);
- if(out!=NULL)
- {
- memcpy(json_string,out,strlen(out));
- free(out);
- }
- }
- return 0;
- }
- //a struct array to CJSON
- int struct_array_to_cJSON(char *text, people worker[])
- {
- cJSON *json,*arrayItem,*item,*object;
- int i;
- for(i=0;i<3;i++)
- {
- printf("i=%d, firstName=%s,lastName=%s,email=%s,age=%d,height=%f\n",
- i,
- worker[i].firstName,
- worker[i].lastName,
- worker[i].email,
- worker[i].age,
- worker[i].height);
- }
- if((text==NULL) || (worker==NULL))
- {
- printf("%s: input is invalid",__func__);
- }
- char * out=NULL;
- cJSON *root=cJSON_CreateObject();
- if (!root)
- {
- printf("Error before: [%s]\n",cJSON_GetErrorPtr());
- return -1;
- }
- else
- {
- cJSON *array_person=cJSON_CreateArray();
- for(i=0;i<3;i++)
- {
- cJSON *obj_person=cJSON_CreateObject();
- cJSON *item=cJSON_CreateString(worker[i].firstName);
- cJSON_AddItemToObject(obj_person,"firstName",item);
- item=cJSON_CreateString(worker[i].lastName);
- cJSON_AddItemToObject(obj_person,"lastName",item);
- item=cJSON_CreateString(worker[i].email);
- cJSON_AddItemToObject(obj_person,"email",item);
- item=cJSON_CreateNumber(worker[i].age);
- cJSON_AddItemToObject(obj_person,"age",item);
- item=cJSON_CreateNumber(worker[i].height);
- cJSON_AddItemToObject(obj_person,"height",item);
- cJSON_AddItemToArray(array_person,obj_person);
- }
- cJSON_AddItemToObject(root,"people",array_person);
- out=cJSON_Print(root);
- printf("out:%s\n",out);
- cJSON_Delete(root);
- if(out!=NULL)
- {
- memcpy(text,out,strlen(out));
- free(out);
- }
- }
- return 0;
- }
- // create CJSON, write file
- void dofile(char *filename)
- {
- FILE *f;
- int len;
- char data[1024];
- f=fopen(filename,"wb");
- fseek(f,0,SEEK_END);
- len=ftell(f);
- fseek(f,0,SEEK_SET);
- printf("read file %s complete, len=%d.\n",filename,len);
- // char str_name[40];
- // int ret = str_to_cJSON(data, str_name);
- people worker[3]={
- {0,"zhong","Jason","bbbb@126.com",0,1.67},
- {1,"","jadena","jadena@126.com",8,1.17},
- {2,"zhu","Juliet","cccc@126.com",36,1.55}
- };
- // struct_to_cJSON(data, &worker[1]);
- struct_array_to_cJSON(data, worker);
- fwrite(data,1,strlen(data),f);
- fclose(f);
- }
3.3.使用cJSON解析JSON字符串
还是在Linux下,使用C语言编程,先实现读文件的功能,然后开始JSON字符串的解析。我们还是一步步来,先从简单的开始,万丈高楼起于平地嘛。
1,下载源码;
可以从如下网站来下载:https://sourceforge.net/projects/cjson/ 。
2,包含cJSON的源码;
下载下来,解压后,从里面找到两个文件(cJSON.c、cJSON.h),复制到我们的工程里面。只需在函数中包含头文件(#include “cJSON.h”),然后和cJSON.c一起编译即可使用。
3,解析一个键值对;
首先是一个简单的键值对字符串,要解析的目标如下:
{"firstName":"Brett"}
要进行解析,也就是要分别获取到键与值的内容。我们很容易就能看出键为firstName,值为Brett,可是,使用cJSON怎么解析呢?
对于这个简单的例子,只需要调用cJSON的三个接口函数就可以实现解析了,这三个函数的原型如下:
cJSON*cJSON_Parse(const char *value);
cJSON*cJSON_GetObjectItem(cJSON *object,const char *string);
voidcJSON_Delete(cJSON *c);
下面按解析过程来描述一次:
(1) 首先调用cJSON_Parse()函数,解析JSON数据包,并按照cJSON结构体的结构序列化整个数据包。使用该函数会通过malloc()函数在内存中开辟一个空间,使用完成需要手动释放。
cJSON*root=cJSON_Parse(json_string);
(2) 调用cJSON_GetObjectItem()函数,可从cJSON结构体中查找某个子节点名称(键名称),如果查找成功可把该子节点序列化到cJSON结构体中。
cJSON*item=cJSON_GetObjectItem(root,"firstName");
(3) 如果需要使用cJSON结构体中的内容,可通过cJSON结构体中的valueint和valuestring取出有价值的内容(即键的值)
本例子中,我们直接访问 item->valuestring 就获取到 "Brett" 的内容了。
(4) 通过cJSON_Delete(),释放cJSON_Parse()分配出来的内存空间。
cJSON_Delete(root);
这样就完成了一次cJSON接口调用,实现了解析工作。使用起来其实也很简单的啊,呵呵。
4,解析一个结构体;
接下来,我们来个复杂一点的,解析一个结构体,要解析的目标如下:
{
"person":
{
"firstName":"z",
"lastName":"jadena",
"email":"jadena@126.com",
"age":8,
"height":1.17
}
}
看起来比一个键值对复杂多了,我们又需要学习新的接口函数了吗?
答案是不需要!
还是那三个函数就可以了。当然,解析的步骤要复杂一些了,下面我按解析过程来描述一次:
(1)根据JSON串中的对象,我们定义一个相应的结构体如下:
typedefstruct
{
char firstName[32];
char lastName[32];
char email[64];
int age;
float height;
} PERSON;
具体的对应关系,一目了然,我就不罗嗦了。让我们直奔主题,解析!
(2)还是调用cJSON_Parse()函数,解析JSON数据包。
cJSON*root=cJSON_Parse(json_string);
(3)调用一次cJSON_GetObjectItem()函数,获取到对象person。
cJSON *object=cJSON_GetObjectItem(root,"person");
(4)对我们刚取出来的对象person,多次调用cJSON_GetObjectItem()函数,来获取对象的成员。此时要注意,不同的成员,访问的方法不一样:
cJSON*item;
PERSONperson;
item=cJSON_GetObjectItem(object,"firstName");
memcpy(person.firstName,item->valuestring,strlen(item->valuestring));
item=cJSON_GetObjectItem(object,"lastName");
memcpy(person.lastName,item->valuestring,strlen(item->valuestring));
item=cJSON_GetObjectItem(object,"email");
memcpy(person.email,item->valuestring,strlen(item->valuestring));
item=cJSON_GetObjectItem(object,"age");
person.age=item->valueint;
item=cJSON_GetObjectItem(object,"height");
person.height=item->valuedouble;
这样,就获取到了对象的全部内容了。
(5) 通过cJSON_Delete(),释放cJSON_Parse()分配出来的内存空间。
cJSON_Delete(root);
至此,我们就使用cJSON接口完成了基于结构体的解析工作。
5,解析结构体数组的JSON串;
最后,我们来个更复杂一些的,来解析一个数组,并且数组的成员是结构体!要解析的JSON串如下:
{
"people":[
{"firstName":"z","lastName":"Jason","email":"bbbb@126.com","height":1.67},
{"lastName":"jadena","email":"jadena@126.com","age":8,"height":1.17},
{"email":"cccc@126.com","firstName":"z","lastName":"Juliet","age":36,"height":1.55}
]
}
此时,我们真的又需要学习新的接口了,一个是获取数组长度,一个是取数组成员,函数原型如下:
int cJSON_GetArraySize(cJSON *array);
cJSON*cJSON_GetArrayItem(cJSON *array,int item);
由于前面已经实现了结构体的解析,这里我们只需要关注下数组的相关调用即可。
(1)调用cJSON_Parse()函数,解析JSON数据包。
(2)调用一次cJSON_GetObjectItem()函数,获取到数组people。
(3)对我们刚取出来的数组people,调用cJSON_GetArraySize()函数,来获取数组中对象的个数。然后,多次调用cJSON_GetArrayItem()函数,逐个读取数组中对象的内容。
(4)通过cJSON_Delete(),释放cJSON_Parse()分配出来的内存空间。
这样,我们就使用cJSON接口完成了结构体数组的解析工作。
详细代码见后文附带例程。
说明:
本文所附带例程,实现了结构体数组的解析,只是一个学习之作,对于初学JSON使用cJSON接口的同学,可以有些借鉴参考的作用。
附带例程:
- #include <stdio.h>
- #include <string.h>
- #include <sys/types.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include "cJSON.h"
- typedef struct
- {
- int id;
- char firstName[32];
- char lastName[32];
- char email[64];
- int age;
- float height;
- }people;
- void dofile(char *filename);/* Read a file, parse, render back, etc. */
- int main(int argc, char **argv)
- {
- // dofile("json_str1.txt");
- // dofile("json_str2.txt");
- dofile("json_str3.txt");
- return 0;
- }
- //parse a key-value pair
- int cJSON_to_str(char *json_string, char *str_val)
- {
- cJSON *root=cJSON_Parse(json_string);
- if (!root)
- {
- printf("Error before: [%s]\n",cJSON_GetErrorPtr());
- return -1;
- }
- else
- {
- cJSON *item=cJSON_GetObjectItem(root,"firstName");
- if(item!=NULL)
- {
- printf("cJSON_GetObjectItem: type=%d, key is %s, value is %s\n",item->type,item->string,item->valuestring);
- memcpy(str_val,item->valuestring,strlen(item->valuestring));
- }
- cJSON_Delete(root);
- }
- return 0;
- }
- //parse a object to struct
- int cJSON_to_struct(char *json_string, people *person)
- {
- cJSON *item;
- cJSON *root=cJSON_Parse(json_string);
- if (!root)
- {
- printf("Error before: [%s]\n",cJSON_GetErrorPtr());
- return -1;
- }
- else
- {
- cJSON *object=cJSON_GetObjectItem(root,"person");
- if(object==NULL)
- {
- printf("Error before: [%s]\n",cJSON_GetErrorPtr());
- cJSON_Delete(root);
- return -1;
- }
- printf("cJSON_GetObjectItem: type=%d, key is %s, value is %s\n",object->type,object->string,object->valuestring);
- if(object!=NULL)
- {
- item=cJSON_GetObjectItem(object,"firstName");
- if(item!=NULL)
- {
- printf("cJSON_GetObjectItem: type=%d, string is %s, valuestring=%s\n",item->type,item->string,item->valuestring);
- memcpy(person->firstName,item->valuestring,strlen(item->valuestring));
- }
- item=cJSON_GetObjectItem(object,"lastName");
- if(item!=NULL)
- {
- printf("cJSON_GetObjectItem: type=%d, string is %s, valuestring=%s\n",item->type,item->string,item->valuestring);
- memcpy(person->lastName,item->valuestring,strlen(item->valuestring));
- }
- item=cJSON_GetObjectItem(object,"email");
- if(item!=NULL)
- {
- printf("cJSON_GetObjectItem: type=%d, string is %s, valuestring=%s\n",item->type,item->string,item->valuestring);
- memcpy(person->email,item->valuestring,strlen(item->valuestring));
- }
- item=cJSON_GetObjectItem(object,"age");
- if(item!=NULL)
- {
- printf("cJSON_GetObjectItem: type=%d, string is %s, valueint=%d\n",item->type,item->string,item->valueint);
- person->age=item->valueint;
- }
- else
- {
- printf("cJSON_GetObjectItem: get age failed\n");
- }
- item=cJSON_GetObjectItem(object,"height");
- if(item!=NULL)
- {
- printf("cJSON_GetObjectItem: type=%d, string is %s, valuedouble=%f\n",item->type,item->string,item->valuedouble);
- person->height=item->valuedouble;
- }
- }
- cJSON_Delete(root);
- }
- return 0;
- }
- //parse a struct array
- int cJSON_to_struct_array(char *text, people worker[])
- {
- cJSON *json,*arrayItem,*item,*object;
- int i;
- json=cJSON_Parse(text);
- if (!json)
- {
- printf("Error before: [%s]\n",cJSON_GetErrorPtr());
- }
- else
- {
- arrayItem=cJSON_GetObjectItem(json,"people");
- if(arrayItem!=NULL)
- {
- int size=cJSON_GetArraySize(arrayItem);
- printf("cJSON_GetArraySize: size=%d\n",size);
- for(i=0;i<size;i++)
- {
- printf("i=%d\n",i);
- object=cJSON_GetArrayItem(arrayItem,i);
- item=cJSON_GetObjectItem(object,"firstName");
- if(item!=NULL)
- {
- printf("cJSON_GetObjectItem: type=%d, string is %s\n",item->type,item->string);
- memcpy(worker[i].firstName,item->valuestring,strlen(item->valuestring));
- }
- item=cJSON_GetObjectItem(object,"lastName");
- if(item!=NULL)
- {
- printf("cJSON_GetObjectItem: type=%d, string is %s, valuestring=%s\n",item->type,item->string,item->valuestring);
- memcpy(worker[i].lastName,item->valuestring,strlen(item->valuestring));
- }
- item=cJSON_GetObjectItem(object,"email");
- if(item!=NULL)
- {
- printf("cJSON_GetObjectItem: type=%d, string is %s, valuestring=%s\n",item->type,item->string,item->valuestring);
- memcpy(worker[i].email,item->valuestring,strlen(item->valuestring));
- }
- item=cJSON_GetObjectItem(object,"age");
- if(item!=NULL)
- {
- printf("cJSON_GetObjectItem: type=%d, string is %s, valueint=%d\n",item->type,item->string,item->valueint);
- worker[i].age=item->valueint;
- }
- else
- {
- printf("cJSON_GetObjectItem: get age failed\n");
- }
- item=cJSON_GetObjectItem(object,"height");
- if(item!=NULL)
- {
- printf("cJSON_GetObjectItem: type=%d, string is %s, value=%f\n",item->type,item->string,item->valuedouble);
- worker[i].height=item->valuedouble;
- }
- }
- }
- for(i=0;i<3;i++)
- {
- printf("i=%d, firstName=%s,lastName=%s,email=%s,age=%d,height=%f\n",
- i,
- worker[i].firstName,
- worker[i].lastName,
- worker[i].email,
- worker[i].age,
- worker[i].height);
- }
- cJSON_Delete(json);
- }
- return 0;
- }
- // Read a file, parse, render back, etc.
- void dofile(char *filename)
- {
- FILE *f;
- int len;
- char *data;
- f=fopen(filename,"rb");
- fseek(f,0,SEEK_END);
- len=ftell(f);
- fseek(f,0,SEEK_SET);
- data=(char*)malloc(len+1);
- fread(data,1,len,f);
- fclose(f);
- printf("read file %s complete, len=%d.\n",filename,len);
- // char str_name[40];
- // int ret = cJSON_to_str(data, str_name);
- // people person;
- // int ret = cJSON_to_struct(data, &person);
- people worker[3]={{0}};
- cJSON_to_struct_array(data, worker);
- free(data);
- }
4.cjson源码解析
4.1.cjson基本数据结构
/* cJSON Types: */
enum {cJSON_False, cJSON_True, cJSON_NULL, cJSON_Number, cJSON_String, cJSON_Array, cJSON_Object, cJSON_IsReference=256};
/* The cJSON structure: */
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;
4.2.节点操作
4.2.1.json 内存管理--hook 管理函数
typedef struct cJSON_Hooks {
void *(*malloc_fn)(size_t sz);
void (*free_fn)(void *ptr);
} cJSON_Hooks;
static void *(*cJSON_malloc)(size_t sz) = malloc;
static void (*cJSON_free)(void *ptr) = free;
void cJSON_InitHooks(cJSON_Hooks* hooks) {
if (!hooks) { /* Reset hooks */
cJSON_malloc = malloc;
cJSON_free = free;
return;
}
cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc;
cJSON_free = (hooks->free_fn)?hooks->free_fn:free;
}
4.2.2.节点操作
在内存管理基础上,可以创建不同类型的节点,这些节点保存的就是json的value值,即key-value键值对。
通过节点操作可以创建的节点连接起来,组成一棵树。
一个json文件可以理解为一棵树形数据结构。
数据存储就是链表操作,就回到了常见数据结构的操作。
这里指的就是节点操作,将节点 a 添加为节点 b 的儿子,将节点 b 从节点 a 的儿子中删除,或者修改节点 a 的值或者查询节点 a 的值等,变为链表基本操作: 增删改查 。
4.2.3.创建节点
/* Internal constructor. */
static cJSON *cJSON_New_Item(void)
{
cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON));
if (node) memset(node,0,sizeof(cJSON));
return node;
}
/* Create basic types: */
cJSON *cJSON_CreateNull(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;}
cJSON *cJSON_CreateTrue(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;}
cJSON *cJSON_CreateFalse(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;}
cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;}
cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;}
cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;}
cJSON *cJSON_CreateArray(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;}
cJSON *cJSON_CreateObject(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;}
/* Create Arrays: */
cJSON *cJSON_CreateIntArray(const int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateFloatArray(const float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateDoubleArray(const double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateNumber(numbers[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && i<count;i++){n=cJSON_CreateString(strings[i]);if(!i)a->child=n;else suffix_object(p,n);p=n;}return a;}
static char* cJSON_strdup(const char* str)
{
size_t len;
char* copy;
len = strlen(str) + 1;
if (!(copy = (char*)cJSON_malloc(len))) return 0;
memcpy(copy,str,len);
return copy;
}
4.2.4.删除节点
删除节点很简单, 先删除儿子,然后清理内存即可。
总结一下就是对于 object 和 array 需要先删除儿子,然后删除自己。
对于 字符串, 需要先释放字符串的内存, 再释放自己这块内存。
对于其他节点,直接释放自己这块内存。
/* Delete a cJSON structure. */
void cJSON_Delete(cJSON *c)
{
cJSON *next;
while (c)
{
next=c->next;
if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child);
if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring);
if (!(c->type&cJSON_StringIsConst) && c->string) cJSON_free(c->string);
cJSON_free(c);
c=next;
}
}
4.2.5.添加儿子节点
添加儿子节点有两种情况,一种是给 object 增加儿子, 一种是给 array 增加儿子。
object 和 array 相比, 仅仅多了一个操作 ,即设置 key .
所以我们可以再 object 中设置完 key 之后再调用 给 array 添加儿子的操作来实现给 object 添加儿子。
具体参考胆码。
/* Utility for array list handling. */
static void suffix_object(cJSON *prev,cJSON *item) {
//两个兄弟的指针互相指向对方
prev->next=item;
item->prev=prev;
}
/* Add item to array/object. */
void cJSON_AddItemToArray(cJSON *array, cJSON *item) {
cJSON *c=array->child;
if (!item) return;
if (!c) {
array->child=item; //之前没有儿子,直接添加
} else {
while (c && c->next) c=c->next; // 先找到最后一个儿子。
suffix_object(c,item); // 添加儿子, c 是 item 的兄弟
}
}
void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {
if (!item) return;
if (item->string) cJSON_free(item->string);//这个 儿子之前有key, 先清理了。
item->string=cJSON_strdup(string); // 设置 key
cJSON_AddItemToArray(object,item); // 添加儿子
}
4.2.6.删除儿子节点
// 节点从双向链表中删除即可
cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) {
cJSON *c=array->child;
while (c && which>0) c=c->next,which--;
if (!c) return 0;
if (c->prev) c->prev->next=c->next;
if (c->next) c->next->prev=c->prev;
if (c==array->child) array->child=c->next;
c->prev=c->next=0;
return c;
}
cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {
int i=0;
cJSON *c=object->child;
while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;
if (c) return cJSON_DetachItemFromArray(object,i);
return 0;
}
4.2.7.查找节点
对于一般类型的item, 我们直接就得到对应的节点.
但是对于 array 和 object , 我们需要查找对应的节点, 所以就需要去查找了。
这个查找算法由 cjson 的储存节点方式决定着。
由于cjson 采用链表储存了, 所以查找当时只能是暴力遍历了。
cJSON *cJSON_GetArrayItem(cJSON *array,int item) {
cJSON *c=array->child;
while (c && item>0) item--,c=c->next;
return c;
}
cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {
cJSON *c=object->child;
while (c && cJSON_strcasecmp(c->string,string)) c=c->next;
return c;
}
4.2.8.修改节点
我们查找到对应的节点了,就可以对节点进行简单的修改了。
什么是简单的修改呢?
节点的类型不是 array 和 object 都可以算是简单类型,可以直接修改修改其值即可。
但是对于 array 和 object, 我们想给他赋值的话,涉及到释放就得内存这个问题。
下面我们来看看 cjson 的实现代码。
/* Replace array/object items with new ones. */
void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {
cJSON *c=array->child;
while (c && which>0) c=c->next,which--;
if (!c) return;
newitem->next=c->next;
newitem->prev=c->prev;
if (newitem->next) newitem->next->prev=newitem;
if (c==array->child) array->child=newitem;
else newitem->prev->next=newitem;
c->next=c->prev=0;
cJSON_Delete(c);
}
void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem) {
int i=0;
cJSON *c=object->child;
while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;
if(c) {
newitem->string=cJSON_strdup(string);
cJSON_ReplaceItemInArray(object,i,newitem);
}
}
4.3.解析概述
4.3.1.解析总的调用操作
/* Parse an object - create a new root, and populate. */
cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated) {
const char *end=0;
cJSON *c=cJSON_New_Item();
ep=0;
if (!c) return 0; /* memory fail */
end=parse_value(c,skip(value));
if (!end) {
cJSON_Delete(c); /* parse failure. ep is set. */
return 0;
}
/* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */
if (require_null_terminated) {
end=skip(end);
if (*end) {
cJSON_Delete(c);
ep=end;
return 0;
}
}
if (return_parse_end) *return_parse_end=end;
return c;
}
/* Default options for cJSON_Parse */
cJSON *cJSON_Parse(const char *value) {
return cJSON_ParseWithOpts(value,0,0);
}
/* Parser core - when encountering text, process appropriately. */
static const char *parse_value(cJSON *item,const char *value) {
if (!value)return 0;/* Fail on null. */
if (!strncmp(value,"null",4)) {
item->type=cJSON_NULL;
return value+4;
}
if (!strncmp(value,"false",5)) {
item->type=cJSON_False;
return value+5;
}
if (!strncmp(value,"true",4)) {
item->type=cJSON_True;
item->valueint=1;
return value+4;
}
if (*value=='\"') {
return parse_string(item,value);
}
if (*value=='-' || (*value>='0' && *value<='9')) {
return parse_number(item,value);
}
if (*value=='[') {
return parse_array(item,value);
}
if (*value=='{') {
return parse_object(item,value);
}
ep=value;
return 0;/* failure. */
}
parse_value 的实现方式很简单, 根据前几个字符来判断写一个类型是什么。如果是 null, false 或 true 设置类型,并返回偏移指针。如果是其他的,则进入对应的函数中。
顺带提一下,用到的一个函数:/* Utility to jump whitespace and cr/lf */
static const char *skip(const char *in) {
while (in && *in && (unsigned char)*in<=32) in++;
return in;
}
skip 用于用于忽略空白,这里跳过了 ascii 值小于 32 的
4.3.2.解析字符串
解析字符串时, 对于特殊字符也应该转义,比如 "\\n" 字符应该转换为 '\n' 这个换行符。当然,如果只有特殊字符转换的话,代码不会又这么长, 对于字符串, 还要支持非 ascii 码的字符, 即 utf8字符。这些字符在字符串中会编码为 \uXXXX 的字符串, 我们现在需要还原为 0-255 的一个字符。
static unsigned parse_hex4(const char *str) {
unsigned h=0;
if (*str>='0' && *str<='9') h+=(*str)-'0';
else if (*str>='A' && *str<='F') h+=10+(*str)-'A';
else if (*str>='a' && *str<='f') h+=10+(*str)-'a';
else return 0;
h=h<<4;
str++;
if (*str>='0' && *str<='9') h+=(*str)-'0';
else if (*str>='A' && *str<='F') h+=10+(*str)-'A';
else if (*str>='a' && *str<='f') h+=10+(*str)-'a';
else return 0;
h=h<<4;
str++;
if (*str>='0' && *str<='9') h+=(*str)-'0';
else if (*str>='A' && *str<='F') h+=10+(*str)-'A';
else if (*str>='a' && *str<='f') h+=10+(*str)-'a';
else return 0;
h=h<<4;
str++;
if (*str>='0' && *str<='9') h+=(*str)-'0';
else if (*str>='A' && *str<='F') h+=10+(*str)-'A';
else if (*str>='a' && *str<='f') h+=10+(*str)-'a';
else return 0;
return h;
}
/* Parse the input text into an unescaped cstring, and populate item. */
static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
static const char *parse_string(cJSON *item,const char *str) {
const char *ptr=str+1;
char *ptr2;
char *out;
int len=0;
unsigned uc,uc2;
if (*str!='\"') {
ep=str; /* not a string! */
return 0;
}
while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++;/* Skip escaped quotes. */
out=(char*)cJSON_malloc(len+1);/* This is how long we need for the string, roughly. */
if (!out) return 0;
ptr=str+1;
ptr2=out;
while (*ptr!='\"' && *ptr) {
if (*ptr!='\\') *ptr2++=*ptr++;
else {
ptr++;
switch (*ptr) {
case 'b':
*ptr2++='\b';
break;
case 'f':
*ptr2++='\f';
break;
case 'n':
*ptr2++='\n';
break;
case 'r':
*ptr2++='\r';
break;
case 't':
*ptr2++='\t';
break;
case 'u': /* transcode utf16 to utf8. */
uc=parse_hex4(ptr+1);
ptr+=4;/* get the unicode char. */
if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0)break;/* check for invalid.*/
if (uc>=0xD800 && uc<=0xDBFF) {/* UTF16 surrogate pairs.*/
if (ptr[1]!='\\' || ptr[2]!='u')break;/* missing second-half of surrogate.*/
uc2=parse_hex4(ptr+3);
ptr+=6;
if (uc2<0xDC00 || uc2>0xDFFF)break;/* invalid second-half of surrogate.*/
uc=0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF));
}
len=4;
if (uc<0x80) len=1;
else if (uc<0x800) len=2;
else if (uc<0x10000) len=3;
ptr2+=len;
switch (len) {
case 4:
*--ptr2 =((uc | 0x80) & 0xBF);
uc >>= 6;
case 3:
*--ptr2 =((uc | 0x80) & 0xBF);
uc >>= 6;
case 2:
*--ptr2 =((uc | 0x80) & 0xBF);
uc >>= 6;
case 1:
*--ptr2 =(uc | firstByteMark[len]);
}
ptr2+=len;
break;
default:
*ptr2++=*ptr;
break;
}
ptr++;
}
}
*ptr2=0;
if (*ptr=='\"') ptr++;
item->valuestring=out;
item->type=cJSON_String;
return ptr;
}
4.3.3.解析数字
/* Parse the input text to generate a number, and populate the result into item. */
static const char *parse_number(cJSON *item,const char *num) {
double n=0,sign=1,scale=0;
int subscale=0,signsubscale=1;
if (*num=='-') sign=-1,num++;/* Has sign? */
if (*num=='0') num++;/* is zero */
if (*num>='1' && *num<='9')don=(n*10.0)+(*num++ -'0');
while (*num>='0' && *num<='9');/* Number? */
if (*num=='.' && num[1]>='0' && num[1]<='9') {
num++; /* Fractional part? */
don=(n*10.0)+(*num++ -'0'),scale--;
while (*num>='0' && *num<='9');
}
if (*num=='e' || *num=='E') {/* Exponent? */
num++;
if (*num=='+') num++;
else if (*num=='-') signsubscale=-1,num++;/* With sign? */
while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0');/* Number? */
}
n=sign*n*pow(10.0,(scale+subscale*signsubscale));/* number = +/- number.fraction * 10^+/- exponent */
item->valuedouble=n;
item->valueint=(int)n;
item->type=cJSON_Number;
return num;
}
4.3.4.解析数组
/* Build an array from input text. */
static const char *parse_array(cJSON *item,const char *value) {
cJSON *child;
if (*value!='[') {
ep=value; /* not an array! */
return 0;
}
item->type=cJSON_Array;
value=skip(value+1);
if (*value==']') return value+1;/* empty array. */
item->child=child=cJSON_New_Item();
if (!item->child) return 0; /* memory fail */
value=skip(parse_value(child,skip(value)));/* skip any spacing, get the value. */
if (!value) return 0;
while (*value==',') {
cJSON *new_item;
if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */
child->next=new_item;
new_item->prev=child;
child=new_item;
value=skip(parse_value(child,skip(value+1)));
if (!value) return 0;/* memory fail */
}
if (*value==']') return value+1;/* end of array */
ep=value;
return 0;/* malformed. */
}
4.3.5.解析对象
/* Build an object from the text. */
static const char *parse_object(cJSON *item,const char *value) {
cJSON *child;
if (*value!='{') {
ep=value; /* not an object! */
return 0;
}
item->type=cJSON_Object;
value=skip(value+1);
if (*value=='}') return value+1;/* empty array. */
item->child=child=cJSON_New_Item();
if (!item->child) return 0;
value=skip(parse_string(child,skip(value)));
if (!value) return 0;
child->string=child->valuestring;
child->valuestring=0;
if (*value!=':') {
ep=value; /* fail! */
return 0;
}
value=skip(parse_value(child,skip(value+1)));/* skip any spacing, get the value. */
if (!value) return 0;
while (*value==',') {
cJSON *new_item;
if (!(new_item=cJSON_New_Item()))return 0; /* memory fail */
child->next=new_item;
new_item->prev=child;
child=new_item;
value=skip(parse_string(child,skip(value+1)));
if (!value) return 0;
child->string=child->valuestring;
child->valuestring=0;
if (*value!=':') {
ep=value; /* fail! */
return 0;
}
value=skip(parse_value(child,skip(value+1)));/* skip any spacing, get the value. */
if (!value) return 0;
}
if (*value=='}') return value+1;/* end of array */
ep=value;
return 0;/* malformed. */
}
4.4.json序列化--打印输出或保存
json 序列化也成为把 json 输出出来。
一般有两种输出:格式化输出,压缩输出。
简单的说就是要不要输出一些空白的问题。
/* Render a cJSON item/entity/structure to text. */
char *cJSON_Print(cJSON *item) {
return print_value(item,0,1);
}
char *cJSON_PrintUnformatted(cJSON *item) {
return print_value(item,0,0);
}
/* Render a value to text. */
static char *print_value(cJSON *item,int depth,int fmt) {
char *out=0;
if (!item) return 0;
switch ((item->type)&255) {
case cJSON_NULL:
out=cJSON_strdup("null");
break;
case cJSON_False:
out=cJSON_strdup("false");
break;
case cJSON_True:
out=cJSON_strdup("true");
break;
case cJSON_Number:
out=print_number(item);
break;
case cJSON_String:
out=print_string(item);
break;
case cJSON_Array:
out=print_array(item,depth,fmt);
break;
case cJSON_Object:
out=print_object(item,depth,fmt);
break;
}
return out;
}
由于基本类型输出的实现比较简单,这里就不多说了,这里只说说输出 对象的实现吧。
假设我们要使用格式化输出, 也就是美化输出。
cjson 的做法不是边分析 json 边输出, 而是预先将要输的内容全部按字符串存在内存中, 最后输出整个字符串。
这对于比较大的 json 来说, 内存就是个问题了。
另外,格式化输出依靠的是节点的深度, 这个也可以优化, 一般宽度超过80 时, 就需要从新的一行算起的。
/* Render an object to text. */
static char *print_object(cJSON *item,int depth,int fmt) {
char **entries=0,**names=0;
char *out=0,*ptr,*ret,*str;
int len=7,i=0,j;
cJSON *child=item->child;
int numentries=0,fail=0;
/* Count the number of entries. */
while (child) numentries++,child=child->next;
/* Explicitly handle empty object case */
if (!numentries) {
out=(char*)cJSON_malloc(fmt?depth+4:3);
if (!out)return 0;
ptr=out;
*ptr++='{';
if (fmt) {
*ptr++='\n';
for (i=0; i<depth-1; i++) *ptr++='\t';
}
*ptr++='}';
*ptr++=0;
return out;
}
/* Allocate space for the names and the objects */
entries=(char**)cJSON_malloc(numentries*sizeof(char*));
if (!entries) return 0;
names=(char**)cJSON_malloc(numentries*sizeof(char*));
if (!names) {
cJSON_free(entries);
return 0;
}
memset(entries,0,sizeof(char*)*numentries);
memset(names,0,sizeof(char*)*numentries);
/* Collect all the results into our arrays: */
child=item->child;
depth++;
if (fmt) len+=depth;
while (child) {
names[i]=str=print_string_ptr(child->string);
entries[i++]=ret=print_value(child,depth,fmt);
if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0);
else fail=1;
child=child->next;
}
/* Try to allocate the output string */
if (!fail) out=(char*)cJSON_malloc(len);
if (!out) fail=1;
/* Handle failure */
if (fail) {
for (i=0; i<numentries; i++) {
if (names[i]) cJSON_free(names[i]);
if (entries[i]) cJSON_free(entries[i]);
}
cJSON_free(names);
cJSON_free(entries);
return 0;
}
/* Compose the output: */
*out='{';
ptr=out+1;
if (fmt)*ptr++='\n';
*ptr=0;
for (i=0; i<numentries; i++) {
if (fmt) for (j=0; j<depth; j++) *ptr++='\t';
strcpy(ptr,names[i]);
ptr+=strlen(names[i]);
*ptr++=':';
if (fmt) *ptr++='\t';
strcpy(ptr,entries[i]);
ptr+=strlen(entries[i]);
if (i!=numentries-1) *ptr++=',';
if (fmt) *ptr++='\n';
*ptr=0;
cJSON_free(names[i]);
cJSON_free(entries[i]);
}
cJSON_free(names);
cJSON_free(entries);
if (fmt) for (i=0; i<depth-1; i++) *ptr++='\t';
*ptr++='}';
*ptr++=0;
return out;
}