1. cJSON使用json
最近一段時間在整理本身進入研究生階段後學習過程當中的一些內容,以前在進行相關代碼調試過程當中,也的確耗費了比較大的精力去搜尋資料,而後整合代碼到本身所須要的系統中,如在stm32中使用cJSON格式,因爲stm32的資源較小,而JSON格式用C語言實現又比較耗費資源,因此在調試的過程當中,要及時釋放內存空間。下面簡單總結一下在stm32中使用cJSON的經驗,時間比較久了,不免有所疏漏,望各位大神指正!另外,在學習總結博客的過程當中,參考了不少網上的博客、文章,在此致以深深的謝意!數組
對於JSON以及Cjson的介紹,能夠參考上一篇博文<JSON以及cJSON介紹>,下面以一個例程來簡單介紹一下如何使用cJSON進行JSON數據格式的解析以及封裝,以下爲主函數:
#include <stdio.h>
#include <stdlib.h>
#include "cJSON.h" // 將cJSON.c、cJSON.h放到工程目錄下,幷包含頭文件
int main(void)
{
char *packtext; // 字符指針,用於接收JSON封裝以後所轉換的字符串
char parsetext[] = // 初始化定義一個字符串,用於JSON的解析
"{\n"
"\"state\":{\n"
"\"desired\":{\n"
"\"message\":\"Hello,cJSON!\"\n"
"},\n"
"\"reported\":{\n"
"\"int_data\":1999,\n"
"\"double_data\":6.666\n"
"}\n"
"},\n"
"\"version\":1234\n"
"}\n";
printf("\n");
parseJSON(parsetext); // JSON解析函數
printf("\n---------------------- Repack the JSON ------------------\n\n");
packtext = packJSON(); // JSON解析函數
parseJSON(packtext);
printf("\n");
return 0;
}
運行結果函數
1.1 JSON解析學習
這裏先介紹cJSON中對於JSON格式的解析;能夠看到,在主函數main中定義了一個字符數組 parsetext,其本質上就是一個JSON的字符串(在實際的stm32系統中,是經過串口與通訊模塊傳輸的),因此在對該JSON進行解析的時候,首先須要將其轉換爲JSON對象的格式,而後才能使用JSON的key來進行數據的讀取操做,如下是對於形式如parsetext的JSON格式的數據解析函數:.net
/* Parse text to JSON, then render back to text, and print! */
void parseJSON(char *text)
{
// 變量的定義
int jversion = 0;
int jint_data = 0;
char *out = NULL;
char *jmessage = NULL;
double jdouble_data = 0;
// cJSON指針的定義
cJSON *json = NULL; // root
cJSON *jver = NULL, *jsta = NULL; // first
cJSON *jdes = NULL, *jrep = NULL; // second
cJSON *jmes = NULL, *jint = NULL, *jdou = NULL; // third
json = cJSON_Parse(text); // 將字符串處理成JSON對象
if (!json) // 若是轉化錯誤,則報錯退出
{
printf("Error before: [%s]\n", cJSON_GetErrorPtr());
}
else
{
/* print the json */
out = cJSON_Print(json); // first string print 將JSON對象「按照格式」打印輸出
printf("%s\n", out);
out = cJSON_PrintUnformatted(json); // second string print將JSON對象「無格式」打印輸出
printf("%s\n", out);
/* get the version of the json */
jver = cJSON_GetObjectItem(json,"version"); //獲取JSON格式中,鍵值爲」version」的對象
jversion = jver->valueint; // 讀取 version的值,version值爲整型數
printf("(1) version: %d\n",jversion);
/* get the state->desired->message of the json */
jsta = cJSON_GetObjectItem(json,"state"); //獲取JSON格式中,鍵值爲」state」的對象
jdes = cJSON_GetObjectItem(jsta,"desired"); //獲取JSON格式中,鍵值爲」desired」的對象
jmes = cJSON_GetObjectItem(jdes,"message");//獲取JSON格式中,鍵值爲」message」的對象
jmessage = jmes->valuestring; // 讀取 message的值,message爲字符串
printf("(2) The message of desired in state is: %s\n",jmessage);
/* get the state->reported value of the json */
jrep = cJSON_GetObjectItem(jsta,"reported"); //獲取JSON格式中,鍵值爲」reported」的對象
jint = cJSON_GetObjectItem(jrep,"int_data"); //獲取JSON格式中,鍵值爲」int_data」的對象
jdou = cJSON_GetObjectItem(jrep,"double_data");//獲取JSON格式中,鍵值爲」double_data」的對象
jint_data = jint->valueint; // 讀取int_data的值
jdouble_data = jdou->valuedouble; // 讀取double_data的值
printf("(3) int_data = %d , double_data = %f \n"\
,jint_data, jdouble_data);
cJSON_Delete(json); // 釋放內存
free(out);
}
}
在上面的JSON解析函數例程中,介紹了cJSON所組成的JSON結構的解析,其中對於嵌套的JSON結構,只須要一層一層的剝開便可。如下對幾個主要函數進行總結(能夠參考【4】【5】):指針
(1)cJSON_Parse函數能夠將字符串解析爲JSON數據包,並按照cJSON結構體的結構序列化整個數據包。使用該函數會經過malloc函數在內存中開闢一個空間,使用完成須要手動釋放;調試
(2)cJSON_GetObjectItem函數可從cJSON結構體中查找某個子鍵名稱,若是查找成功可把該子節點序列化到cJSON結構體中;orm
(3)若是須要使用cJSON結構體中的內容,可經過cJSON結構體中的valueint、valuedouble和valuestring取出有價值的內容(即鍵的值);對象
(4)cJSON_Delete函數用於釋放JSON所佔的內存空間;blog
(5) JSON 格式的數據,本質上是一個字符串,但這個時候沒法當成普通的字符串進行使用,須要調用 cJSON_PrintUnformatted(json) 或者 cJSON_Print(json)將JSON對象轉換成普通的字符串,而且都是以該json對象的根爲基點。其中二者的區別便是:一個是沒有格式的:也就是轉換出的字符串中間不會有"\n" "\t"之類的東西存在,在串傳輸的時候,使用「無格式」,而cJSON_Print(json)打印出來是人看起來舒服、明瞭的JSON格式。
1.2 JSON封裝
/* Pack the JSON as the parseJSON, return the char pointer of the JSON string */
char *packJSON()
{
cJSON *root,*staj,*desj,*repj; // cJSON指針
char *out;
root=cJSON_CreateObject(); // 建立root對象,返回值爲cJSON指針
staj=cJSON_CreateObject();
desj=cJSON_CreateObject();
repj=cJSON_CreateObject();
cJSON_AddItemToObject(staj, "desired", desj); // 向staj中添加一個對象 desj
cJSON_AddStringToObject(desj, "message", "cJSON Test!");// 向desj中添加一個字符串,鍵爲「message」,鍵值爲「cJSON Test!」
cJSON_AddItemToObject(staj, "reported", repj);
cJSON_AddNumberToObject(repj, "int_data",9999); // 向repj中添加一個整數,鍵爲「int_data」,鍵值爲「9999」
cJSON_AddNumberToObject(repj, "double_data",8.888); // 向repj中添加一個浮點數,鍵爲「double_data」,鍵值爲「8.888」
cJSON_AddItemToObject(root, "state", staj); //將staj對象添加到 root中,鍵爲「state」
cJSON_AddNumberToObject(root, "version", 4321); // 向root中添加整數,鍵爲「version」,鍵值爲4321
out = cJSON_Print(root); // 將 root 轉化爲普通字符串,並返回指針
cJSON_Delete(root); //釋放內存
return out;
}
在上面JSON封裝的例程中,介紹瞭如何使用cJSON封裝相似於parsetext的JSON數據,並向其中的鍵賦值,下面簡單介紹一下主要使用的函數:
(1)cJSON_CreateObject函數用於建立一個JSON對象,返回值爲一個指向所建立JSON的cJSON指針,使用完成後,注意要使用cJSON_Delete函數進行內存的釋放;
(2)cJSON_AddItemToObject函數用於向JSON對象中添加一個對象,而且指定了對象的鍵;
(3)cJSON_AddStringToObject函數用於向JSON對象中添加一個字符串,而且指定了字符串的鍵以及鍵值;
(4)cJSON_AddNumberToObject函數用於向JSON對象中添加一個數據,能夠是整型數,也能夠是浮點數。
OK,對於cJSON的使用就先介紹這麼多了;另外,看其餘資料,還可使用Cjson結構體中的type來進行對鍵的判斷(對象、數、字符串等)。
2. cJSON在stm32中的使用
對於cJSON的使用,主要是用來實現stm32鏈接一個上網模塊來與雲平臺進行交互,而云平臺上面所解析的數據格式是JSON格式,所以在stm32中,須要實現將數據封裝成JSON格式,而後經過串口將數據由上網模塊(WiFi、LTE等)上報給雲平臺。
cJSON是由C語言所編寫的,因此理論上是能夠跨平臺使用的,可是因爲stm32中內存資源的限制,所以在使用完JSON結構數據以後,要儘快釋放內存;另外,還須要對 malloc、free、以及size_t等進行從新定義,其中,對於malloc以及free的修改以下:
(1) cJSON.c中須要修改的地方
static void *(*cJSON_malloc)(size_t sz)= malloc; static void (*cJSON_free)(void *ptr) = free; ----------------------------------- modify -------------------------------------static void *(*cJSON_malloc)(size_t sz)= mymalloc; // modifystatic void (*cJSON_free)(void *ptr) = myfree; // modifyvoid cJSON_InitHooks(cJSON_Hooks* hooks){ if (!hooks) { /* Reset hooks */ cJSON_malloc = malloc; cJSON_free = free; return; } cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc; cJSON_free = (hooks->free_fn)?hooks->free_fn:free; }----------------------------------- modify -------------------------------------void cJSON_InitHooks(cJSON_Hooks* hooks){ if (!hooks) { /* Reset hooks */ cJSON_malloc = mymalloc; // modify cJSON_free = myfree; // modify return; } cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:mymalloc; // modify cJSON_free = (hooks->free_fn)?hooks->free_fn:myfree; // modify }