一款轻量级的JSON解析库,用cJSON让你看清王者荣耀

news/2024/11/16 16:48:05/

JSON是一种轻量级的数据格式,应用广泛。在C/C++应用中也常常作为配置文件或者数据的存储

JSON语法规则

JSON对象是一个无序的"名称/值"键值对的集合:

  • 以"{“开始,以”}"结束,允许「嵌套使用」

  • 每个「名称和值成对出现」,名称和值之间使用":"分隔;

  • 键值对之间用","分隔

  • 在这些字符前后允许存在无意义的空白符;

对于键值,可以有如下值:

  • 一个新的「json对象」

  • 「数组」:使用"[“和”]"表示

  • 「数字」:直接表示,可以是整数,也可以是浮点数

  • 「字符串」:使用引号"表示

  • 「字面值」:false、null、true中的一个(必须是小写)

示例如下:

[{"ename": 105,"cname": "廉颇","title": "正义爆轰","new_type": 0,"hero_type": 3,"skin_name": "正义爆轰|地狱岩魂"
}, {"ename": 106,"cname": "小乔","title": "恋之微风","new_type": 0,"hero_type": 2,"skin_name": "恋之微风|万圣前夜|天鹅之梦|纯白花嫁|缤纷独角兽"
}]

cJSON

cJSON下载使用

cJSON是使用ANSI C编写的「超轻量级」的JSON解析器,因此在C中也常常是不二之选。

cJSON项目托管在Github上,仓库地址如下:

https://github.com/DaveGamble/cJSON

使用Git命令将其拉取到本地:

git clone https://github.com/DaveGamble/cJSON.git

从Github拉取cJSON源码后,文件非常多,但是其中cJSON的源码文件只有两个:

  • cJSON.h

  • cJSON.c

使用的时候,只需要将这两个文件复制到工程目录,然后包含头文件cJSON.h即可,如下:

#include "cJSON.h"

关键数据结构

cJSON的关键数据结构如下:

typedef struct cJSON {  //cJSON结构体struct cJSON*next,*prev;           /*后驱节点和前驱节点*/struct cJSON *child;                   /*孩子节点*/int type;                                     /* 键的类型*/char *valuestring;                       /*字符串值*/int valueint;                                /* 整数值*/double valuedouble;                    /* 浮点数值*/char *string;                               /* 键的名字*/
} cJSON;

json是一种组织良好的数据格式,因而JSON中的内容解析后,都可以通过以上数据结构进行处理。

例如,对于下面的json内容:

{"name":"编程学习基地","site":"https://www.deroy.cn","age":1
}

解析后,site将会是name的next节点,并且它的键类型是字符串。

cJSON数据解析

常用接口函数

用于「将字符串解析成json对象」,若失败则返回NULL。

cJSON *cJSON_Parse(const char *value);

用于「获取json对象中的某个节点」,若失败,返回NULL,成功则返回该节点对象。

cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);

用于释放json对象相关内存。

void   cJSON_Delete(cJSON *c);

如果JSON数据的值是数组,可以通过下面接口获取JSON 数组大小和数组里面的JSON 对象

int cJSON_GetArraySize(const cJSON *array);
cJSON * cJSON_GetArrayItem(const cJSON *array, int index);

解析步骤

  • 「将JSON文件内容读取到buffer」

  • 「通过cJSON接口解析buffer中的字符串」

  • 「获取JSON指定字段」

为了将JSON文件的内容读取到buffer,需要知道文件的大小:

size_t get_file_size(const char *filepath)
{/*check input para*/if(NULL == filepath)return 0;struct stat filestat;memset(&filestat,0,sizeof(struct stat));/*get file information*/if(0 == stat(filepath,&filestat))return filestat.st_size;elsereturn 0;
}

然后申请一段内存,将文件中的文本读取到buffer中:

char *read_file_to_buf(const char *filepath)
{/*check input para*/if(NULL == filepath){return NULL;}/*get file size*/size_t size = get_file_size(filepath);if(0 == size)return NULL;/*malloc memory*/char *buf = malloc(size+1);if(NULL == buf)return NULL;memset(buf,0,size+1);/*read string from file*/FILE *fp = fopen(filepath,"r");size_t readSize = fread(buf,1,size,fp);if(readSize != size){/*read error*/free(buf);buf = NULL;}buf[size] = 0;return buf;
}

再根据前面提到的解析流程,我们的JSON预解析函数如下:

cJSON *prepare_parse_json(const char *filePath)
{/*check input para*/if(NULL == filePath){printf("input para is NULL\n");return NULL;}/*read file content to buffer*/char *buf = read_file_to_buf(filePath);if(NULL == buf){printf("read file to buf failed\n");return NULL;}/*parse JSON*/cJSON *pTemp = cJSON_Parse(buf);free(buf);buf = NULL;return pTemp;
}

解析示例

#include<stdio.h>
#include<stdlib.h>
#include<sys/stat.h>
#include<string.h>
#include"cJSON.h"size_t get_file_size(const char *filepath)
{/*check input para*/if(NULL == filepath)return 0;struct stat filestat;memset(&filestat,0,sizeof(struct stat));/*get file information*/if(0 == stat(filepath,&filestat))return filestat.st_size;elsereturn 0;
}char *read_file_to_buf(const char *filepath)
{/*check input para*/if(NULL == filepath){return NULL;}/*get file size*/size_t size = get_file_size(filepath);if(0 == size)return NULL;/*malloc memory*/char *buf = (char*)malloc(size+1);if(NULL == buf)return NULL;memset(buf,0,size+1);/*read string from file*/FILE *fp = fopen(filepath,"r");size_t readSize = fread(buf,1,size,fp);if(readSize != size){/*read error*/free(buf);buf = NULL;}buf[size] = 0;return buf;
}
/**/
cJSON *prepare_parse_json(const char *filePath)
{/*check input para*/if(NULL == filePath){printf("input para is NULL\n");return NULL;}/*read file content to buffer*/char *buf = read_file_to_buf(filePath);if(NULL == buf){printf("read file to buf failed\n");return NULL;}/*parse JSON*/cJSON *pTemp = cJSON_Parse(buf);free(buf);buf = NULL;return pTemp;
}
int main(void)
{char *filename = "herolist.json";cJSON *pJson = NULL;cJSON *pTemp = NULL;cJSON *pVal = NULL;/*创建cJSON对象*/pJson = prepare_parse_json(filename);if(NULL == pJson){printf("parse json failed\n");return -1;}/*获取cJSON数组数量*/int num = cJSON_GetArraySize(pJson);/*遍历每一个cJSON数组元素*/for(int index=0;index<num;index++){/*获取cJSON数组中的第index个cJSON对象*/pTemp = cJSON_GetArrayItem(pJson,index);/*获取cJSON对象中的key值为ename的对象*/pVal = cJSON_GetObjectItem(pTemp,"ename");printf("ename:%d\n",pVal->valueint);pVal = cJSON_GetObjectItem(pTemp,"cname");printf("cname:%s\n",pVal->valuestring);pVal = cJSON_GetObjectItem(pTemp,"title");printf("title:%s\n",pVal->valuestring);pVal = cJSON_GetObjectItem(pTemp,"new_type");printf("new_type:%d\n",pVal->valueint);pVal = cJSON_GetObjectItem(pTemp,"hero_type");printf("hero_type:%d\n",pVal->valueint);pVal = cJSON_GetObjectItem(pTemp,"skin_name");printf("skin_name:%s\n\n",pVal->valuestring);printf("====================================\n\n");}/*释放内存*/cJSON_Delete(pJson);pJson = NULL;return 0;
}
gcc -o mian main.c cJSON.c

 

windows下VS可以运行,但是因为编码格式问题,我另写了一套程序

为了让输出看起来舒服,改了点格式,输出如下(删减),源码获取发送关键字【王者荣耀】

|ename |cname  |title  |new_type |hero_type |skin_name|
-----------------------------------------------------------------------------------------------------------
|105 |廉颇  |正义爆轰 |0  |3  |正义爆轰|地狱岩魂|
-----------------------------------------------------------------------------------------------------------
|106 |小乔  |恋之微风 |0  |2  |恋之微风|万圣前夜|天鹅之梦|纯白花嫁|缤纷独角兽|
-----------------------------------------------------------------------------------------------------------
|107 |赵云  |苍天翔龙 |0  |1  |苍天翔龙|忍●炎影|未来纪元|皇家上将|嘻哈天王|白执事|引擎之心|
-----------------------------------------------------------------------------------------------------------
|108 |墨子  |和平守望 |0  |2  |和平守望|金属风暴|龙骑士|进击墨子号|
-----------------------------------------------------------------------------------------------------------

JSON数据封装

封装方法

封装JSON数据的过程,其实就是「创建链表」「向链表中添加节点」的过程。

首先来讲述一下链表中的一些术语:

  • 「头指针」:指向链表头结点的指针;

  • 「头结点」:不存放有效数据,方便链表操作;

  • 「首节点」:第一个存放有效数据的节点;

  • 「尾节点」:最后一个存放有效数据的节点;

封装步骤

明白了这几个概念之后,我们开始讲述「创建一段完整的JSON数据」,即如何「创建一条完整的链表」

  • ① 「创建头指针」

 cJSON* cjson_test = NULL;
  • ② 「创建头结点」,并将头指针指向头结点:

cjson_test = cJSON_CreateObject();
  • ③ 尽情的向链表中「添加节点」

/* 添加一个值为 null 的布尔类型的JSON数据(添加一个链表节点) */
cJSON_AddNullToObject(cJSON * const object, const char * const name);
/* 添加一个值为 true 的布尔类型的JSON数据(添加一个链表节点) */
cJSON_AddTrueToObject(cJSON * const object, const char * const name);
/* 添加一个值为 False 的布尔类型的JSON数据(添加一个链表节点) */
cJSON_AddFalseToObject(cJSON * const object, const char * const name);
/* 添加一个值为布尔类型的JSON数据 0:false 非0:true (添加一个链表节点) */
cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);
/* 添加一条数值类型的JSON数据(添加一个链表节点) */
cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);
/* 添加一条字符串类型的JSON数据(添加一个链表节点) */
cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);
/* 添加一行数据(添加一个链表节点) */
cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);
/* 添加一个空对象(添加一个链表节点) */
cJSON_AddObjectToObject(cJSON * const object, const char * const name);
/* 添加一个空数组(添加一个链表节点) */
cJSON_AddArrayToObject(cJSON * const object, const char * const name);
/* 添加一个嵌套的JSON对象/数组(添加一个链表节点) */
cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item);

cJSON还提供了将JSON对象转换成字符串输出到终端

char *cJSON_Print(const cJSON *item);

封装示例

#include <stdio.h>
#include "cJSON.h"
int main(void)
{cJSON* cjson_test = NULL;cJSON* cjson_address = NULL;cJSON* cjson_skill = NULL;char* str = NULL;/* 创建一个JSON数据对象(链表头结点) */cjson_test = cJSON_CreateObject();/* 添加一个值为 null 的布尔类型的JSON数据(添加一个链表节点) */cJSON_AddNullToObject(cjson_test, "null_test");/* 添加一个值为 true 的布尔类型的JSON数据(添加一个链表节点) */cJSON_AddTrueToObject(cjson_test,"true_test");/* 添加一个值为 False 的布尔类型的JSON数据(添加一个链表节点) */cJSON_AddFalseToObject(cjson_test, "false_test");/* 添加一个值为布尔类型的JSON数据 0:false 非0:true (添加一个链表节点) */cJSON_AddBoolToObject(cjson_test, "bool_test", 0);/* 添加一条整数类型的JSON数据(添加一个链表节点) */cJSON_AddNumberToObject(cjson_test, "int_test", 22);/* 添加一条浮点类型的JSON数据(添加一个链表节点) */cJSON_AddNumberToObject(cjson_test, "double_test", 55.5);/* 添加一条字符串类型的JSON数据(添加一个链表节点) */cJSON_AddStringToObject(cjson_test, "str_test", "我是字符串");/* 添加一行任意数据(添加一个链表节点) */cJSON_AddRawToObject(cjson_test, "key", "任意数据");/* 添加一个空对象(添加一个链表节点) */cJSON_AddObjectToObject(cjson_test, "objet");/* 添加一个嵌套的JSON对象(添加一个链表节点) */cjson_address = cJSON_CreateObject();cJSON_AddStringToObject(cjson_address, "country", "China");cJSON_AddNumberToObject(cjson_address, "zip-code", 111111);cJSON_AddItemToObject(cjson_test, "address", cjson_address);/* 添加一个空数组(添加一个链表节点) */cJSON_AddArrayToObject(cjson_test, "Array");/* 添加一个数组类型的JSON数据(添加一个链表节点) */cjson_skill = cJSON_CreateArray();cJSON_AddItemToArray(cjson_skill, cJSON_CreateString( "C" ));cJSON_AddItemToArray(cjson_skill, cJSON_CreateString( "C++" ));cJSON_AddItemToArray(cjson_skill, cJSON_CreateString( "Python" ));cJSON_AddItemToArray(cjson_skill, cJSON_CreateString( "Java"));cJSON_AddItemToObject(cjson_test, "skill", cjson_skill);/* 打印JSON对象(整条链表)的所有数据 */str = cJSON_Print(cjson_test);printf("%s\n", str);return 0;
}

输出结果:

{"null_test": null,"true_test": true,"false_test": false,"bool_test": false,"int_test": 22,"double_test": 55.5,"str_test": "我是字符串","key": 任意数据,"objet": {},"address": {"country": "China","zip-code": 111111},"Array": [],"skill": ["C", "C++", "Python", "Java"]
}

 


http://www.ppmy.cn/news/249144.html

相关文章

CS8900A-IQ3Z

CS8900A-IQ3Z 制造商&#xff1a;Cirrus logic 无铅情况/RoHs 无铅/符合RoHs 技术参考 工作温度 -40C ~ 85C 封装/外壳 100-LQFP 协议 以太网 功能 控制器 接口 ISA 标准 10 Base-T PHY 电压 - 电源 3V&#xff0c;5V 电流 - 电源 95mA

arduino控制JQ8900-16P语音模块

首先要感谢stm32通过 一线串口通信控制JQ8900-16P语音模块的作者 我就是根据这位博主的代码改写的&#xff0c;但是碰到了意想不到的问题。其实代码是写对了&#xff0c;语音模块是单独供电&#xff0c;这个供电的地要和arduino的地共用。&#xff08;我第一天测试的时候其实也…

51单片机与JQ8900语音播报模块

51单片机JQ8900通信 51单片机串口通信方式实现功能实现功能接线图实物 51单片机 使用的是普中51-单核-A5的板子 串口通信方式 、 实现功能 测试语音播报模块一线串口模式。 在上位机&#xff08;电脑&#xff09;端输入字符1&#xff08;通过串口调试助手&#xff09;&…

(转)嵌入式系统中常见的网卡驱动比较(CS8900A,RTL8019,DM9000)

1. CS8900A CS8900芯片是Cirrus Logic公司生产的一种局域网处理芯片&#xff0c;在嵌入式领域中使用非常常见。它的封装是100-pin TQFP&#xff0c;内部集成了在片RAM、10BASE-T收发滤波器&#xff0c;并且提供8位和16位两种接口。CS8900与ARM芯片按照16位方式连接&#xf…

Linux-2.6.20的cs8900驱动分析(一)

本博客转载于&#xff1a;http://blog.chinaunix.net/xmlrpc.php?rblog/article&uid22535114&id1773574 一、初始化阶段 网络初始化被调用的路径为&#xff1a; init->do_basic_setup->do_initcalls->net_olddevs_init->ethif_probe2->probe_list2-&g…

NANO2410 CS8900 debugging

1. 内核启动过程中CS8900驱动始终没有加载 最终发现跟device注册有关&#xff0c;修改mach_smdk2410.c文件如下&#xff1a; /* CS8900 */static struct resource s3c_cs89x0_resources[] {[0] DEFINE_RES_MEM(0x19000000, SZ_1M),[1] DEFINE_RES_IRQ(IRQ_EINT9), };stati…

Linux-2.6.20的cs8900驱动分析(三)

本博客转载于&#xff1a;http://blog.chinaunix.net/xmlrpc.php?rblog/article&uid22535114&id1773576 三、net_rx和net_send_packet 3.1 net_rx 在这部分将介绍cs8900驱动的两个最重要的函数&#xff0c;内核通过该两个函数实现了数据的收发。net_rx函数的主要功能是…

CS8900A网络驱动程序移植

上图为开发板上的网卡原理图&#xff0c;可得到&#xff1a; 1.CS8900A的访问基址为0x19000000 2.总线位宽为16 3.中断引脚为EINT9 驱动文件drivers/net/cs89x0.c既可以编进内核&#xff0c;也可以编译为一个可加载模块。编译进内核时&#xff0c;它的入口函数为cs89x0_pro…