使用CJSON 解析JSON 结构体数组【典型】
一、CJSON数据结构定义#define cJSON_False 0#define cJSON_True 1#define cJSON_NULL 2#define cJSON_Number 3#define cJSON_String 4#define cJSON_Array 5//数组#define cJSON_Object 6 //对象or键名typedef struct cJSON {stru
1.CJSON数据结构定义
#define cJSON_False 0
#define cJSON_True 1
#define cJSON_NULL 2
#define cJSON_Number 3
#define cJSON_String 4
#define cJSON_Array 5 //数组
#define cJSON_Object 6 //对象or单键名
typedef struct cJSON {
struct cJSON *next,*prev; /*遍历数组或对象链的前向或后向链表指针*/
struct cJSON *child; /*数组或对象的子节点*/
int type; /* key键的类型,上面宏定义的7中之一*/
char *valuestring; /*字符串值, if type==cJSON_String */
int valueint; /* 整型数值, if type==cJSON_Number */
double valuedouble; /* 浮点数值, if type==cJSON_Number */
char *string; /* key键的名字 */
} cJSON;
说明:
1、cJSON是使用链表来存储数据的,其访问方式很像一颗树。每一个节点可以有兄弟节点,通过next/prev指针来查找,它类似双向链表;每个节点也可以有孩子节点,通过child指针来访问,进入下一层。只有节点是对象或数组时才可以有孩子节点。
2、type是键(key)的类型,一共有7种取值,分别是:False,Ture,NULL,Number,String,Array(数组),Object(对象或单键名)。
若是Number类型,则valueint或valuedouble中存储着值。若期望的是int,则访问valueint,若期望的是double,则访问valuedouble,可以得到值。若是String类型的,则valuestring中存储着值,可以访问valuestring得到值。
3、string中存放的是这个节点的名字,可理解为key的名称。
2.常用的解析函数
/* 第一个
*函数功能:将一个JSON数据包序列化,并开辟堆内存存储获取的cJSON对象;
*返回值:成功返回一个指向cJSON对象的指针;失败则返回NULL
*/
extern cJSON *cJSON_Parse(const char *value);//从源数据中,获取未整理的JSON对象
/* 第二个
*函数功能:根据单键名(cjson对象)获取对应的值
*参数:
*objec:第一个函数中获取的句柄
* string:需要获取的对象
*返回值:
*/
extern cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);
/* 第三个
*函数功能:
*返回值:
*/
extern int cJSON_GetArraySize(cJSON *array);//获取cjson对象数组成员的个数
/* 第四个
*函数功能:根据下标获取cjosn对象数组中对应元素的对象
*参数:
array 数组名
item 数组的下标元素
*返回值:元素的键值;失败则返回NULL
*/
extern cJSON *cJSON_GetArrayItem(cJSON *array,int item);
/* 第五个
*作用:与Parse成对出现,释放Parse操作时,产生的位于堆中cJSON结构体内存;
*返回值:无
*/
extern void cJSON_Delete(cJSON *c);//删除cjson对象,释放链表占用的内存空间
3.解析:包含两个键值对的结构体(单层)
第一层结构体;第二层键值对
步骤:Parse格式化==》cJSON_GetObjectItem根据键名获取键值
char text[]="{\"name\":\"Tom\",\"age\":18}";
cJSON * root = cJSON_Parse(text);
if(!root) {
printf("no json\n");
return -1;
}
//name
cJSON *name = cJSON_GetObjectItem(root, "name");
if (!name) {
printf("no name!\n");
return -1;
} else{
printf("%s\n",name->valuestring);
}
//age
cJSON *age = cJSON_GetObjectItem(root, "age");
if (!age) {
printf("no age!\n");
return -1;
} else{
printf("%d\n",age->valueint);
}
cJSON_Delete(root);
4.解析:键值对嵌套
{
"semantic": {
"slots": {
"name": "张三"
}
},
"rc": 0,
"operation": "CALL",
"service": "telephone",
"text": "打电话给张三"
}
5.解析:包含键值对结构体的数组
第一层数组;第二层结构体;第三层键值对;
步骤:Parse格式化==》IsArray判断是否数组==》GetArraySize获取数组元素个数==》cJSON_GetArrayItem按照下标检索数组元素==》cJSON_GetObjectItem根据元素中的单键名获取键值
int main(){
char text[]="[{\"name\":\"Tom1\",\"age\":18},{\"name\":\"Tom2\",\"age\":19},{\"name\":\"Tom3\",\"age\":20},{\"name\":\"Tom4\",\"age\":21}]";
cJSON * root = cJSON_Parse(text);
if(!root) {
printf("no json\n");
return -1;
}
if (!cJSON_IsArray(root)){
printf("no Array\n");
return -1;
}
//数组长度
int array_size = cJSON_GetArraySize(root);
cJSON *item;
cJSON *name;
cJSON *age;
for(int i=0; i< array_size; i++) {
item = cJSON_GetArrayItem(root, i);
name = cJSON_GetObjectItem(item, "name");
if (!name) {
printf("no name!\n");
return -1;
}
printf("%s\t",name->valuestring);
age = cJSON_GetObjectItem(item, "age");
if (!age) {
printf("no age!\n");
return -1;
}
printf("%d\n",age->valueint);
}
return 0;
}
cJSON_Delete(root);
6.解析结构体数组的JSON串
与5的区别在于,第一层是键值对(值:是结构体数组)
{
"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}
]
}
(1)调用cJSON_Parse()函数,解析JSON数据包。
(2)调用一次cJSON_GetObjectItem()函数,获取到数组people。【重点区别!!】
(3)对刚取出来的数组people,调用cJSON_GetArraySize()函数,来获取数组中对象的个数。然后,多次调用cJSON_GetArrayItem()函数,逐个读取数组中对象的内容。
(4)通过cJSON_Delete(),释放cJSON_Parse()分配出来的内存空间。
例程如下:例程较长,见文章末尾。
7.混合解析:键值对结构体+数组混合嵌套
{
"results":
[
{
"currentConfirmedCount":3247,
"currentConfirmedIncr":-37,
"confirmedCount":127336,
"confirmedIncr":39,
"suspectedCount":9919,
"suspectedIncr":23,
"curedCount":118392,
"curedIncr":76,
"deadCount":5697,
"deadIncr":0,
"seriousCount":500,
"seriousIncr":-10,
"globalStatistics":
{
"currentConfirmedCount":40083221,
"confirmedCount":255222728,
"curedCount":210013003,
"deadCount":5126504,
"currentConfirmedIncr":370347,
"confirmedIncr":485719,
"curedIncr":108221,
"deadIncr":7151,
"yesterdayConfirmedCountIncr":485714
},
"generalRemark":"",
"remark1":"易感人群:人群普遍易感。老年人及有基础疾病者感染后病情较重,儿童及婴幼儿也有发病",
"remark2":"潜伏期:一般为 3~7 天,最长不超过 14 天,潜伏期内可能存在传染性,其中无症状病例传染性非常罕见",
"remark3":"宿主:野生动物,可能为中华菊头蝠",
"remark4":"",
"remark5":"",
"note1":"病毒:SARS-CoV-2,其导致疾病命名 COVID-19",
"note2":"传染源:新冠肺炎的患者。无症状感染者也可能成为传染源。",
"note3":"传播途径:经呼吸道飞沫、接触传播是主要的传播途径。气溶胶传播和消化道等传播途径尚待明确。",
"updateTime":1637145079410
}
],
"success":true
}
而当待解析数据同时包含了“结构体数组”时:
第一步,先利用cJSON_GetArraySize获取数组元素个数;
第二步,使用cJSON_GetArrayItem(数组名,元素下标)方式来检索数组中对应的元素;
第三步,判断该元素是否包含了直接的对象,即该元素的类型是否是cJSON_Object;
第四步,使用cJSON_GetObjectItem(元素名,单键名)的方式获取单键名所对应的键值;
仅解析部分:
int parseData(uint8_t *str)
{
int ret = 0;
cJSON *root, *result_arr;
cJSON *result, *global;
time_t updateTime;
struct tm *time;
root = cJSON_Parse((const char *)str); //创建JSON解析对象,返回JSON格式是否正确
if (root != 0)
{
rt_kprintf("JSON format ok, start parse!!!\n");
//通过调用cJSON_GetObjectItem,获取到数组results!!!!
result_arr = cJSON_GetObjectItem(root, "results");//results是服务器下发JSON数据的数组名
if(result_arr->type == cJSON_Array)
{
// rt_kprintf("result is array\n");
//由于提前知道数组results只包含1个元素,因此不用再调用cJSON_GetArraySize来获取元素个数,而是直接读第0个元素的值
result = cJSON_GetArrayItem(result_arr, 0);//在数组result_arr中检索第0个元素
if(result->type == cJSON_Object)//如果第0个元素的类型是“对象”
{
// rt_kprintf("result_arr[0] is object\n");
/* china data parse */
dataChina.currentConfirmedCount = cJSON_GetObjectItem(result, "currentConfirmedCount")->valueint;
dataChina.currentConfirmedIncr = cJSON_GetObjectItem(result, "currentConfirmedIncr")->valueint;
dataChina.confirmedCount = cJSON_GetObjectItem(result, "confirmedCount")->valueint;
dataChina.confirmedIncr = cJSON_GetObjectItem(result, "confirmedIncr")->valueint;
dataChina.curedCount = cJSON_GetObjectItem(result, "curedCount")->valueint;
dataChina.curedIncr = cJSON_GetObjectItem(result, "curedIncr")->valueint;
dataChina.deadCount = cJSON_GetObjectItem(result, "deadCount")->valueint;
dataChina.deadIncr = cJSON_GetObjectItem(result, "deadIncr")->valueint;
rt_kprintf("**********china ncov data**********\n");
rt_kprintf("%-23s: %8d, %-23s: %8d\n", "currentConfirmedCount", dataChina.currentConfirmedCount, "currentConfirmedIncr", dataChina.currentConfirmedIncr);
rt_kprintf("%-23s: %8d, %-23s: %8d\n", "confirmedCount", dataChina.confirmedCount, "confirmedIncr", dataChina.confirmedIncr);
rt_kprintf("%-23s: %8d, %-23s: %8d\n", "curedCount", dataChina.curedCount, "curedIncr", dataChina.curedIncr);
rt_kprintf("%-23s: %8d, %-23s: %8d\n", "deadCount", dataChina.deadCount, "deadIncr", dataChina.deadIncr);
/* global data parse */
global = cJSON_GetObjectItem(result, "globalStatistics");
if(global->type == cJSON_Object)
{
dataGlobal.currentConfirmedCount = cJSON_GetObjectItem(global, "currentConfirmedCount")->valueint;
dataGlobal.currentConfirmedIncr = cJSON_GetObjectItem(global, "currentConfirmedIncr")->valueint;
dataGlobal.confirmedCount = cJSON_GetObjectItem(global, "confirmedCount")->valueint;
dataGlobal.confirmedIncr = cJSON_GetObjectItem(global, "confirmedIncr")->valueint;
dataGlobal.curedCount = cJSON_GetObjectItem(global, "curedCount")->valueint;
dataGlobal.curedIncr = cJSON_GetObjectItem(global, "curedIncr")->valueint;
dataGlobal.deadCount = cJSON_GetObjectItem(global, "deadCount")->valueint;
dataGlobal.deadIncr = cJSON_GetObjectItem(global, "deadIncr")->valueint;
rt_kprintf("\n**********global ncov data**********\n");
rt_kprintf("%-23s: %8d, %-23s: %8d\n", "currentConfirmedCount", dataGlobal.currentConfirmedCount, "currentConfirmedIncr", dataGlobal.currentConfirmedIncr);
rt_kprintf("%-23s: %8d, %-23s: %8d\n", "confirmedCount", dataGlobal.confirmedCount, "confirmedIncr", dataGlobal.confirmedIncr);
rt_kprintf("%-23s: %8d, %-23s: %8d\n", "curedCount", dataGlobal.curedCount, "curedIncr", dataGlobal.curedIncr);
rt_kprintf("%-23s: %8d, %-23s: %8d\n", "deadCount", dataGlobal.deadCount, "deadIncr", dataGlobal.deadIncr);
} else return 1;
/* 毫秒级时间戳转字符串 */
updateTime = (time_t )(cJSON_GetObjectItem(result, "updateTime")->valuedouble / 1000);
updateTime += 8 * 60 * 60; /* UTC8校正 */
time = localtime(&updateTime);
/* 格式化时间 */
strftime(dataChina.updateTime, 20, "%m-%d %H:%M", time);
rt_kprintf("update: %s\r\n", dataChina.updateTime);/* 06-24 11:21 */
gui_show_ncov_data(dataChina, dataGlobal);//???屏幕输出相关!!!!
} else return 1;
} else return 1;
rt_kprintf("\nparse complete \n");
}
else
{
rt_kprintf("JSON format error:%s\n", cJSON_GetErrorPtr()); //输出json格式错误信息
return 1;
}
cJSON_Delete(root);
return ret;
}
8.两种数组的解析
8.1包含多个键值对的结构体数组
以上格式可以使用如下解析方法:
【重点:利用数组或对象的后向链表指针】
cJSON *ip_arry = cJSON_GetObjectItem( clientlist, "iplist"); //clientlist 是使用 cjson对象
if( NULL != ip_arry ){
cJSON *client_list = ip_arry->child;
while( client_list != NULL ){
char * ip = cJSON_GetObjectItem( client_list , "ip")->valuestring ;
char * mask = cJSON_GetObjectItem( client_list , "mask")->valuestring ;
printf("ip: %s mask: %s",ip,mask);
client_list = client_list->next ;
}
}
8.2 包含多个键值的单键数组
方法类似结构体数组
解析方法如下:第一步获取键名,第二步获取数组个数,第三步按照元素下标获取元素对象!
cJSON *MAC_arry = cJSON_GetObjectItem( clientlist, "Maclist");
if( MAC_arry != NULL ){
int array_size = cJSON_GetArraySize ( MAC_arry );
for( iCnt = 0 ; iCnt < array_size ; iCnt ++ ){
cJSON * pSub = cJSON_GetArrayItem(MAC_arry, iCnt);
if(NULL == pSub ){ continue ; }
char * ivalue = pSub->valuestring ;
printf("Maclist[%d] : %s",iCnt,ivalue);
}
}
附件:
本文6段的例程
#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);
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)