JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。JSON在互联网相关开发中用得很多,在我们嵌入式中用得也不少。最近小编在项目中也有用到,分享分享。
简单的JSON格式数据如:
{ "name":"xxx", "num":xxx, "c_score":xxx }
这里我们需要知道一个概念:键值对。比如:
"name":"xxx"
像这样子的就是一对键值对。
当我们作为发送方时,我们要把xxx这些有用的数据组合成JSON格式的数据发送给接收方;当我们作为接收方时,我们需要从这一堆JSON数据中解析出xxx这些有用的数据拿来使用。
简单的JSON数据,我们使用C语言的一些字符串操作相关的库函数也是可以做到组包和解析的,但是一些稍微复杂一点的JSON,可能就没那么好操作了。
这时候我们可以借助一个第三方库——cJSON库,可以很方便来做数据组包及解析。
下面,我们通过实例来分享使用cJSON库来做数据组包及数据解析。
一、组包与解析示例
1、确定协议数据
在实际开发中,要把JSON数据作为通信的数据,自然要先确定通信双方要交互的数据有哪些,如有需要还需编写形成协议文档。协议文档包含要传输的数据,数据类型等信息。
比如:
2、组JSON数据包示例
从控制台输入一些学生信息,组合成字符串格式的JSON数据包,然后再输出至控制台。
操作示例:
首先,我们先从仓库下载cJSON源码,文件夹内容如:
我们只需要把cJSON.c、cJSON.h两个文件复制到我们工程的根目录下就可以使用,如:
从cJSON.h可以看到其给我们提供了很多接口:
本例中我们重点关注如下几个接口即可:
cJSON_CreateObject:创建JSON对象,{}扩起来的 cJSON_CreateString:创建字符串 cJSON_CreateNumber:创建int类型数据 cJSON_AddItemToObject:添加到JSON对象中 cJSON_Print:呈现为标准的JSON格式 cJSON_PrintUnformatted:呈现为去掉空格的JSON格式 cJSON_Delete:JSON对象删除,做一些释放内存的工作
我们创建的的组包函数如下:
staticchar*StudentsData_Packet(pStudentDef _Stu) { char*res_string=NULL;//返回值 cJSON*name=NULL;//名字 cJSON*num=NULL;//学号 cJSON*c_score=NULL;//C语言分数 /*创建一个JSON对象,{}扩起来*/ cJSON*obj=cJSON_CreateObject(); if(obj==NULL) { gotoend; } /*创建"name":"xxx"键值对*/ name=cJSON_CreateString(_Stu->name); if(name==NULL) { gotoend; } cJSON_AddItemToObject(obj,"name",name); /*创建"num":207键值对*/ num=cJSON_CreateNumber(_Stu->num); if(name==NULL) { gotoend; } cJSON_AddItemToObject(obj,"num",num); /*创建"c_score":95键值对*/ c_score=cJSON_CreateNumber(_Stu->c_score); if(name==NULL) { gotoend; } cJSON_AddItemToObject(obj,"c_score",c_score); res_string=cJSON_Print(obj);//呈现为JSON格式 //res_string=cJSON_PrintUnformatted(obj);//呈现为无格式 if(res_string==NULL) { fprintf(stderr,"Failed to print monitor. "); } /*异常情况统一Delete(free)*/ end: cJSON_Delete(obj); returnres_string; }
详细解释见注释。我们重点看一下cJSON_Print与cJSON_PrintUnformatted这两个接口。
这两个接口的差别就是组合成的JSON数据是否有空格。
有空格的JSON数据,即用cJSON_Print时的效果为:
无空格的JSON数据,即用cJSON_PrintUnformatted时的效果为:
如果想要输出查看时,当然是用cJSON_Print比较方便查看;如果是实际通信时,当然是用cJSON_PrintUnformatted会比较好,毕竟去掉空格就可以减小一定程度的通信负担。
完整代码:
/* 作者:ZhengN 公众号:嵌入式大杂烩 */ #include#include #include #include"cJSON.h" #defineSTU_NAME_LEN32 /*学生结构体*/ typedefstruct_Student { charname[STU_NAME_LEN];//名字 intnum;//学号 intc_score;//C语言分数 }StudentDef,*pStudentDef; /*内部函数声明*/ staticchar*StudentsData_Packet(pStudentDef _Stu); /******************************************************************************************************** **函数:main **------------------------------------------------------------------------------------------------------ **参数: **说明: **返回: ********************************************************************************************************/ intmain(void) { charname[STU_NAME_LEN]={0}; intnum=0; intc_score=0; StudentDef stu; intstu_count=0; inti=0; /*学生总人数*/ printf("Please input number of student:"); scanf("%d",&stu_count); while(i++< stu_count) { /* 名字 */ printf("Please input name: "); scanf("%s", name); if (strlen(name) < STU_NAME_LEN) { strncpy((char*)&stu.name, name, strlen(name)+1); } else { printf("The name is too long "); } /* 学号 */ printf("Please input num (0~100): "); scanf("%d", &num); stu.num = num; /* C语言分数 */ printf("Please input c_score (0~100): "); scanf("%d", &c_score); stu.c_score = c_score; /* 输出JSON格式的学生数据 */ printf("%s ", StudentsData_Packet(&stu)); } return 0; } /******************************************************************************************************** ** 函数: StudentsData_Packet, 学生JSON格式数据组包 **------------------------------------------------------------------------------------------------------ ** 参数: _Stu:组student json数据包需要的数据 ** 说明: ** 返回: JSON格式的字符串 ********************************************************************************************************/ static char *StudentsData_Packet(pStudentDef _Stu) { char *res_string = NULL; // 返回值 cJSON *name = NULL; // 名字 cJSON *num = NULL; // 学号 cJSON *c_score = NULL; // C语言分数 /* 创建一个JSON对象,{}扩起来 */ cJSON *obj = cJSON_CreateObject(); if (obj == NULL) { goto end; } /* 创建 "name": "xxx" 键值对 */ name = cJSON_CreateString(_Stu->name); if(name==NULL) { gotoend; } cJSON_AddItemToObject(obj,"name",name); /*创建"num":207键值对*/ num=cJSON_CreateNumber(_Stu->num); if(name==NULL) { gotoend; } cJSON_AddItemToObject(obj,"num",num); /*创建"c_score":95键值对*/ c_score=cJSON_CreateNumber(_Stu->c_score); if(name==NULL) { gotoend; } cJSON_AddItemToObject(obj,"c_score",c_score); res_string=cJSON_Print(obj);//呈现为JSON格式 //res_string=cJSON_PrintUnformatted(obj);//呈现为无格式 if(res_string==NULL) { fprintf(stderr,"Failed to print monitor. "); } /*异常情况统一Delete(free)*/ end: cJSON_Delete(obj); returnres_string; }
3、解析JSON数据包示例
我们把我们想要解析的数据放到一个student_data.txt文件中,然后读取其内容拿来解析,最后输出解析结果。
student_data.txt的内容如:
解析结果:
关于这个示例我们需要关注的接口有:
cJSON_Parse:JSON解析函数,解析{}得到里面的内容 cJSON_GetObjectItemCaseSensitive:从对象中获取键“字符串”。不分大小写 cJSON_IsString:判断是否是字符串 cJSON_IsNumber:判断是否是整形数 cJSON_Delete:JSON对象删除,做一些释放内存的工作
我们创建的解析函数如下:
staticvoidStudentsData_Parse(pStudentDef _Stu,constchar*_JsonStudnetData) { cJSON*student_json=NULL;//student_json操作对象,可代表{}扩起来的内容 cJSON*name=NULL; cJSON*num=NULL; cJSON*c_score=NULL; /*开始解析*/ student_json=cJSON_Parse(_JsonStudnetData); if(NULL==student_json) { constchar*error_ptr=cJSON_GetErrorPtr(); if(error_ptr!=NULL) { fprintf(stderr,"Error before:%s ",error_ptr); } gotoend; } /*解析获取name得值*/ name=cJSON_GetObjectItemCaseSensitive(student_json,"name"); if(cJSON_IsString(name)&&(name->valuestring!=NULL)) { memcpy(&_Stu->name,name->valuestring,strlen(name->valuestring)); } /*解析获取num的值*/ num=cJSON_GetObjectItemCaseSensitive(student_json,"num"); if(cJSON_IsNumber(num)) { _Stu->num=num->valueint; } /*解析获取c_score的值*/ c_score=cJSON_GetObjectItemCaseSensitive(student_json,"c_score"); if(cJSON_IsNumber(c_score)) { _Stu->c_score=c_score->valueint; } end: cJSON_Delete(student_json); }
解释见注释。
完整代码:
/* 作者:ZhengN 公众号:嵌入式大杂烩 */ #include#include #include #include"cJSON.h" #defineSTU_NAME_LEN32 /*学生结构体*/ typedefstruct_Student { charname[STU_NAME_LEN];//名字 intnum;//学号 intc_score;//C语言分数 }StudentDef,*pStudentDef; /*内部函数声明*/ staticvoidStudentsData_Parse(pStudentDef _Stu,constchar*_JsonStudnetData); staticvoidPrintParseResult(constpStudentDef _Stu); /******************************************************************************************************** **函数:main **------------------------------------------------------------------------------------------------------ **参数: **说明: **返回: ********************************************************************************************************/ intmain(void) { StudentDef stu={0};//保存解析后的数据 intfile_len=0;//文件长度 FILE*fp=NULL;//文件句柄 char*data=NULL;//用于保存从文件读出的数据 /*文件操作*/ if((fp=fopen("student_data.txt","r"))==NULL) { printf("Open file error! "); exit(EXIT_FAILURE); } fseek(fp,0,SEEK_END);//文件位置指针指向文件末尾 file_len=ftell(fp);//获取文末相对于文首的偏移值 fseek(fp,0,SEEK_SET);//文件位置指针指向文首 data=(char*)malloc(file_len+1);//为data申请堆内存 fread(data,file_len,1,fp);//读取文件数据保存至data fclose(fp);//关闭文件 /*解析*/ StudentsData_Parse(&stu,(constchar*)data); /*打印输出解析结果*/ PrintParseResult(&stu); /*释放内存*/ free(data);//防止内存泄漏 data=NULL;//防止出现野指针 return0; } /******************************************************************************************************** **函数:StudentsData_Parse,JOSN格式学生期末数据解析 **------------------------------------------------------------------------------------------------------ **参数:_JsonStudnetData:JSON数据_Stu:保存解析出的有用数据 **说明: **返回: ********************************************************************************************************/ staticvoidStudentsData_Parse(pStudentDef _Stu,constchar*_JsonStudnetData) { cJSON*student_json=NULL;//student_json操作对象,可代表{}扩起来的内容 cJSON*name=NULL; cJSON*num=NULL; cJSON*c_score=NULL; /*开始解析*/ student_json=cJSON_Parse(_JsonStudnetData); if(NULL==student_json) { constchar*error_ptr=cJSON_GetErrorPtr(); if(error_ptr!=NULL) { fprintf(stderr,"Error before:%s ",error_ptr); } gotoend; } /*解析获取name得值*/ name=cJSON_GetObjectItemCaseSensitive(student_json,"name"); if(cJSON_IsString(name)&&(name->valuestring!=NULL)) { memcpy(&_Stu->name,name->valuestring,strlen(name->valuestring)); } /*解析获取num的值*/ num=cJSON_GetObjectItemCaseSensitive(student_json,"num"); if(cJSON_IsNumber(num)) { _Stu->num=num->valueint; } /*解析获取c_score的值*/ c_score=cJSON_GetObjectItemCaseSensitive(student_json,"c_score"); if(cJSON_IsNumber(c_score)) { _Stu->c_score=c_score->valueint; } end: cJSON_Delete(student_json); } /******************************************************************************************************** **函数:PrintParseResult,打印输出解析结果 **------------------------------------------------------------------------------------------------------ **参数: **说明: **返回: ********************************************************************************************************/ staticvoidPrintParseResult(constpStudentDef _Stu) { printf("name:%s,num:%d,c_score:%d ",_Stu->name,_Stu->num,_Stu->c_score); }
二、综合示例
上一节中我们的组包、解析demo都是分开测试的,这一节再分享一个两个demo综合起来的demo:
运行演示:
json_print.c完整代码:
/* 作者:ZhengN 公众号:嵌入式大杂烩 */ #include#include #include #include #include"cJSON.h" #defineSTU_NAME_LEN32 /*学生结构体*/ typedefstruct_Student { charname[STU_NAME_LEN];//名字 intnum;//学号 intc_score;//C语言分数 }StudentDef,*pStudentDef; /*内部函数声明*/ staticStudentDefStudentData_Prepare(void); staticchar*StudentsData_Packet(pStudentDef _Stu); staticvoidStudentData_Send(constchar*_data); /******************************************************************************************************** **函数:main **------------------------------------------------------------------------------------------------------ **参数: **说明: **返回: ********************************************************************************************************/ intmain(void) { StudentDef stu={0}; char*stu_data=NULL; intstu_count=0; inti=0; /*需要登记的学生总人数*/ printf("Please input number of student:"); scanf("%d",&stu_count); while(i++< stu_count) { /* 准备数据 */ stu = StudentData_Prepare(); /* JSON格式数据组包 */ stu_data = StudentsData_Packet(&stu); /* 发送数据 */ StudentData_Send(stu_data); } return 0; } /******************************************************************************************************** ** 函数: StudentData_Prepare, 准备组包需要的数据 **------------------------------------------------------------------------------------------------------ ** 参数: ** 说明: ** 返回: 获取得到的数据 ********************************************************************************************************/ static StudentDef StudentData_Prepare(void) { char name[STU_NAME_LEN] = {0}; int num = 0; int c_score = 0; StudentDef stu; /* 名字 */ printf("Please input name: "); scanf("%s", name); if (strlen(name) < STU_NAME_LEN) { strncpy((char*)&stu.name, name, strlen(name)+1); } else { printf("The name is too long "); } /* 学号 */ printf("Please input num (0~100): "); scanf("%d", &num); stu.num = num; /* C语言分数 */ printf("Please input c_score (0~100): "); scanf("%d", &c_score); stu.c_score = c_score; return stu; } /******************************************************************************************************** ** 函数: StudentsData_Packet, JSON格式数据组包 **------------------------------------------------------------------------------------------------------ ** 参数: _Stu:组student json数据包需要的数据 ** 说明: ** 返回: JSON格式的字符串 ********************************************************************************************************/ static char *StudentsData_Packet(pStudentDef _Stu) { char *res_string = NULL; // 返回值 cJSON *name = NULL; // 名字 cJSON *num = NULL; // 学号 cJSON *c_score = NULL; // C语言分数 /* 创建一个JSON对象,{}扩起来 */ cJSON *obj = cJSON_CreateObject(); if (obj == NULL) { goto end; } /* 创建 "name": "xxx" 键值对 */ name = cJSON_CreateString(_Stu->name); if(name==NULL) { gotoend; } cJSON_AddItemToObject(obj,"name",name); /*创建"num":207键值对*/ num=cJSON_CreateNumber(_Stu->num); if(name==NULL) { gotoend; } cJSON_AddItemToObject(obj,"num",num); /*创建"c_score":95键值对*/ c_score=cJSON_CreateNumber(_Stu->c_score); if(name==NULL) { gotoend; } cJSON_AddItemToObject(obj,"c_score",c_score); res_string=cJSON_Print(obj);//呈现为JSON格式 //res_string=cJSON_PrintUnformatted(obj);//呈现为无格式 if(res_string==NULL) { fprintf(stderr,"Failed to print monitor. "); } /*异常情况统一Delete(free)*/ end: cJSON_Delete(obj); returnres_string; } /******************************************************************************************************** **函数:StudentData_Send,JSON格式字符串数据组包发送 **------------------------------------------------------------------------------------------------------ **参数:_data:要发送的数据 **说明: **返回: ********************************************************************************************************/ staticvoidStudentData_Send(constchar*_data) { WSADATA wd; SOCKET ClientSock; SOCKADDR_INServerSockAddr; printf("%s ",_data); /*初始化操作sock需要的DLL*/ WSAStartup(MAKEWORD(2,2),&wd); /*向服务端发起请求*/ memset(&ServerSockAddr,0,sizeof(ServerSockAddr)); ServerSockAddr.sin_family=AF_INET; ServerSockAddr.sin_addr.s_addr=inet_addr("127.0.0.1"); ServerSockAddr.sin_port=htons(1314); /*创建客户端socket*/ if(-1==(ClientSock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))) { printf("socket error! "); exit(EXIT_FAILURE); } if(-1==connect(ClientSock,(SOCKADDR*)&ServerSockAddr,sizeof(SOCKADDR))) { printf("connect error! "); exit(EXIT_FAILURE); } /*发送数据到服务端*/ send(ClientSock,_data,strlen(_data),0); /*关闭套接字*/ closesocket(ClientSock); }
json_parse.c完整代码:
左右滑动查看全部代码>>>
/* 作者:ZhengN 公众号:嵌入式大杂烩 */ #include#include #include #include #include"cJSON.h" #defineSTU_NAME_LEN32 /*学生结构体*/ typedefstruct_Student { charname[STU_NAME_LEN];//名字 intnum;//学号 intc_score;//C语言分数 }StudentDef,*pStudentDef; /*内部函数声明*/ staticchar*StudentsData_Recv(void); staticvoidStudentsData_Parse(pStudentDef _Stu,constchar*_JsonStudnetData); staticvoidPrintParseResult(constpStudentDef _Stu); staticvoidSaveParseResult(constpStudentDef _Stu); /*内部全局变量*/ staticFILE*stu_fp=NULL; /******************************************************************************************************** **函数:main **------------------------------------------------------------------------------------------------------ **参数: **说明: **返回: ********************************************************************************************************/ intmain(void) { StudentDef stu={0}; char*recv_data; while(1) { /*接收数据*/ recv_data=StudentsData_Recv(); /*解析*/ StudentsData_Parse(&stu,(constchar*)recv_data); /*打印输出解析结果*/ PrintParseResult(&stu); /*保存数据到文件*/ SaveParseResult(&stu); /*释放内存*/ free(recv_data);//防止内存泄漏 recv_data=NULL;//防止出现野指针 } return0; } /******************************************************************************************************** **函数:StudentsData_Recv,接收数据 **------------------------------------------------------------------------------------------------------ **参数: **说明: **返回: ********************************************************************************************************/ staticchar*StudentsData_Recv(void) { WSADATA wd; SOCKADDR_IN ServerSockAddr; intrecv_len=0; char*recv_buf=(char*)malloc(512); staticSOCKET ServerSock,ClientSock; staticSOCKADDR ClientAddr; staticintaddr_size=0; staticintrun_count=0; /*以下操作执行只一次就可以*/ if(0==run_count) { /*初始化操作sock需要的DLL*/ WSAStartup(MAKEWORD(2,2),&wd); /*创建服务端socket*/ if(-1==(ServerSock=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP))) { printf("server socket error! "); exit(EXIT_FAILURE); } /*设置服务端信息*/ memset(&ServerSockAddr,0,sizeof(ServerSockAddr));//给结构体ServerSockAddr清零 ServerSockAddr.sin_family=AF_INET;//使用IPv4地址 ServerSockAddr.sin_addr.s_addr=inet_addr("127.0.0.1");//本机IP地址 ServerSockAddr.sin_port=htons(1314);//端口 /*绑定套接字*/ if(-1==bind(ServerSock,(SOCKADDR*)&ServerSockAddr,sizeof(SOCKADDR))) { printf("bind error! "); exit(EXIT_FAILURE); } printf("bind ok! "); /*进入监听状态*/ if(-1==listen(ServerSock,10)) { printf("listen error! "); exit(EXIT_FAILURE); } printf("listen ok! "); addr_size=sizeof(SOCKADDR); } run_count++; /*监听客户端请求,accept函数返回一个新的套接字,发送和接收都是用这个套接字*/ if(-1==(ClientSock=accept(ServerSock,(SOCKADDR*)&ClientAddr,&addr_size))) { printf("client socket error! "); exit(EXIT_FAILURE); } /*接受客户端的返回数据*/ memset(recv_buf,0,512); recv_len=recv(ClientSock,recv_buf,512,0); printf("%s ",recv_buf); /*关闭客户端套接字*/ closesocket(ClientSock); /*返回获取得到JSON数据*/ return(char*)recv_buf; } /******************************************************************************************************** **函数:StudentsData_Parse,JOSN格式学生期末数据解析 **------------------------------------------------------------------------------------------------------ **参数:_JsonStudnetData:JSON数据_Stu:保存解析出的有用数据 **说明: **返回: ********************************************************************************************************/ staticvoidStudentsData_Parse(pStudentDef _Stu,constchar*_JsonStudnetData) { cJSON*student_json=NULL;//student_json操作对象,可代表{}扩起来的内容 cJSON*name=NULL; cJSON*num=NULL; cJSON*c_score=NULL; /*开始解析*/ student_json=cJSON_Parse(_JsonStudnetData); if(NULL==student_json) { constchar*error_ptr=cJSON_GetErrorPtr(); if(error_ptr!=NULL) { fprintf(stderr,"Error before:%s ",error_ptr); } gotoend; } /*解析获取name得值*/ name=cJSON_GetObjectItemCaseSensitive(student_json,"name"); if(cJSON_IsString(name)&&(name->valuestring!=NULL)) { memset(&_Stu->name,0,STU_NAME_LEN*sizeof(char)); memcpy(&_Stu->name,name->valuestring,strlen(name->valuestring)); } /*解析获取num的值*/ num=cJSON_GetObjectItemCaseSensitive(student_json,"num"); if(cJSON_IsNumber(num)) { _Stu->num=num->valueint; } /*解析获取c_score的值*/ c_score=cJSON_GetObjectItemCaseSensitive(student_json,"c_score"); if(cJSON_IsNumber(c_score)) { _Stu->c_score=c_score->valueint; } end: cJSON_Delete(student_json); } /******************************************************************************************************** **函数:PrintParseResult,打印输出解析结果 **------------------------------------------------------------------------------------------------------ **参数: **说明: **返回: ********************************************************************************************************/ staticvoidPrintParseResult(constpStudentDef _Stu) { printf("name:%s,num:%d,c_score:%d ",_Stu->name,_Stu->num,_Stu->c_score); } /******************************************************************************************************** **函数:SaveParseResult,保存解析结果 **------------------------------------------------------------------------------------------------------ **参数:_Stu:需要保存的数据 **说明: **返回: ********************************************************************************************************/ staticvoidSaveParseResult(constpStudentDef _Stu) { charwrite_buf[512]={0}; staticintstu_count=0; /*以可在文件末尾追加内容的方式打开文件*/ if((stu_fp=fopen("ParseResult.txt","a+"))==NULL) { printf("Open file error! "); returnexit(EXIT_FAILURE); } /*按指定格式写入文件*/ snprintf(write_buf,512,"name:%s,num:%d,c_score:%d ",_Stu->name,_Stu->num,_Stu->c_score); size_tlen=fwrite((char*)write_buf,1,strlen(write_buf),stu_fp); /*文件位置指针偏移*/ fseek(stu_fp,len*stu_count,SEEK_SET); stu_count++; /*关闭文件*/ fclose(stu_fp); }
编译命令:
左右滑动查看全部代码>>>
gcc json_print.c cJSON.c-o json_print.exe-lwsocket32 gcc json_parse.c cJSON.c-o json_parse.exe-lwsocket32
综合demo加了socket相关代码,本篇笔记主要介绍JSON数据的组包及解析。
审核编辑:刘清
-
控制器
+关注
关注
112文章
16332浏览量
177798 -
嵌入式
+关注
关注
5082文章
19104浏览量
304777 -
数据通信
+关注
关注
2文章
431浏览量
33754 -
Stu
+关注
关注
0文章
2浏览量
7307 -
JSON
+关注
关注
0文章
117浏览量
6963
原文标题:嵌入式实用知识之JSON数据
文章出处:【微信号:玩点嵌入式,微信公众号:玩点嵌入式】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
相关推荐
评论