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++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++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文章
16203瀏覽量
177413 -
嵌入式
+關注
關注
5068文章
19019瀏覽量
303292 -
數據通信
+關注
關注
2文章
429瀏覽量
33737 -
Stu
+關注
關注
0文章
2瀏覽量
7299 -
JSON
+關注
關注
0文章
117瀏覽量
6940
原文標題:嵌入式實用知識之JSON數據
文章出處:【微信號:玩點嵌入式,微信公眾號:玩點嵌入式】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論