使用CJSON 解析JSON 结构体数组【典型】

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":"[email protected]","height":1.67},
{"lastName":"jadena","email":"[email protected]","age":8,"height":1.17},
{"email":"[email protected]","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 解析JSON 结构体数组【典型】_第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 解析JSON 结构体数组【典型】_第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  
#include  
#include  
#include  
#include  
 
#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;itype,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);  

你可能感兴趣的:(嵌入式,STM32,C-base,CJSON,JSON串,结构体数组,键值对,IOT)