javascript
JSON数据格式解析库(cJSON、Jansson)的使用在STM32上移植和使用
json | json-c使用入門 這篇講的也不錯,抽空看下(網(wǎng)絡(luò)傳輸json數(shù)據(jù))
https://www.bilibili.com/video/av669454528?p=3&spm_id_from=pageDriver
目錄
- 輕量級C語言JSON解析庫
- 1.JSON與cJSON
- JSON —— 輕量級的數(shù)據(jù)格式
- JSON語法規(guī)則
- cJSON
- 2.cJSON數(shù)據(jù)結(jié)構(gòu)和設(shè)計思想
- 3.JSON數(shù)據(jù)封裝
- 封裝方法
- 輸出JSON數(shù)據(jù)
- 封裝數(shù)據(jù)和打印數(shù)據(jù)示例
- 4.cJSON數(shù)據(jù)解析
- 解析方法
- 解析示例
- 注意事項
- 5.cJSON使用過程中的內(nèi)存問題
- 內(nèi)存及時釋放
- 內(nèi)存鉤子
- cJSON在STM32移植和使用
- 概要
- 硬件準備
- 軟件準備
- 實際操作步驟
- 總結(jié)
- 快速生成JSON數(shù)據(jù)(sprintf)和解析JSON數(shù)據(jù)(strstr)技巧
- 1. 生成JSON數(shù)據(jù)
- 2. 解析JSON數(shù)據(jù)
- Keil環(huán)境下Jansson解析庫的使用
輕量級C語言JSON解析庫
參考博文:cJSON使用詳細教程 | 一個輕量級C語言JSON解析器
作者:Mculover666
地址:https://mculover666.blog.csdn.net/article/details/103796256?spm=1001.2014.3001.5502
1.JSON與cJSON
JSON —— 輕量級的數(shù)據(jù)格式
JSON 全稱 JavaScript Object Notation,即 JS對象簡譜,是一種輕量級的數(shù)據(jù)格式。
它采用完全獨立于編程語言的文本格式來存儲和表示數(shù)據(jù),語法簡潔、層次結(jié)構(gòu)清晰,易于人閱讀和編寫,同時也易于機器解析和生成,有效的提升了網(wǎng)絡(luò)傳輸效率。
JSON語法規(guī)則
JSON對象是一個無序的"名稱/值"鍵值對的集合:
- 以"{“開始,以”}"結(jié)束,允許嵌套使用,即對象里面嵌套對象;
- 每個名稱和值成對出現(xiàn)(名稱又叫做鍵,所以又叫鍵值對),名稱和值之間使用":"分隔;
- 鍵值對之間用","分隔
- 在這些字符前后允許存在無意義的空白符;
對于鍵值,可以有如下值:
- 一個新的json對象
- 數(shù)組:使用"[“和”]"表示
- 數(shù)字:直接表示,可以是整數(shù),也可以是浮點數(shù)
- 字符串:使用引號"表示
- 字面值:false、null、true中的一個(必須是小寫)
注意:
詳細的JSON數(shù)據(jù)格式參照:太極創(chuàng)客JSON基礎(chǔ)
示例如下:
{"name": "mculover666","age": 22,"weight": 55.5,"address":{ //值是一個對象,即對象里面嵌套對象"country": "China","zip-code": 111111},"skill": ["c", "Java", "Python"],//數(shù)組"student": false //布爾 }cJSON
cJSON是一個使用C語言編寫的JSON數(shù)據(jù)解析器,具有超輕便,可移植,單文件的特點,使用MIT開源協(xié)議。
cJSON項目托管在Github上,倉庫地址如下:
https://github.com/DaveGamble/cJSON
使用Git命令將其拉取到本地:
git clone https://github.com/DaveGamble/cJSON.git從Github拉取cJSON源碼后,文件非常多,但是其中cJSON的源碼文件只有兩個:
- cJSON.h
- cJSON.c
使用的時候,只需要將這兩個文件復(fù)制到工程目錄,然后包含頭文件cJSON.h即可,如下:
#include "cJSON.h"2.cJSON數(shù)據(jù)結(jié)構(gòu)和設(shè)計思想
cJSON的設(shè)計思想從其數(shù)據(jù)結(jié)構(gòu)上就能反映出來。
cJSON使用cJSON結(jié)構(gòu)體來表示一個JSON數(shù)據(jù),定義在cJSON.h中,源碼如下:
/* The cJSON structure: */ typedef struct cJSON {/* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */struct cJSON *next;struct cJSON *prev;/* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */struct cJSON *child;/* The type of the item, as above. */int type;/* The item's string, if type==cJSON_String and type == cJSON_Raw */char *valuestring;/* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */int valueint;/* The item's number, if type==cJSON_Number */double valuedouble;/* The item's name string, if this item is the child of, or is in the list of subitems of an object. */char *string; } cJSON;cJSON的設(shè)計很巧妙。
首先,它不是將一整段JSON數(shù)據(jù)抽象出來,而是將其中的一條JSON數(shù)據(jù)抽象出來,也就是一個鍵值對,用上面的結(jié)構(gòu)體 strcut cJSON 來表示,其中用來存放值的成員列表如下:
- String:用于表示該鍵值對的名稱;
- type:用于表示該鍵值對中值的類型;
- valuestring:如果鍵值類型(type)是字符串,則將該指針指向鍵值;
- valueint:如果鍵值類型(type)是整數(shù),則將該指針指向鍵值;
- valuedouble:如果鍵值類型(type)是浮點數(shù),則將該指針指向鍵值;
其次,一段完整的JSON數(shù)據(jù)中由很多鍵值對組成,并且涉及到鍵值對的查找、刪除、添加,所以使用鏈表來存儲整段JSON數(shù)據(jù),如上面的代碼所示:
- next指針:指向下一個鍵值對
- prev指針:指向上一個鍵值對
最后,因為JSON數(shù)據(jù)支持嵌套,所以一個鍵值對的值會是一個新的JSON數(shù)據(jù)對象(一條新的鏈表),也有可能是一個數(shù)組,方便起見,在cJSON中,數(shù)組也表示為一個數(shù)組對象,用鏈表存儲,所以:
在鍵值對結(jié)構(gòu)體中,當該鍵值對的值是一個嵌套的JSON數(shù)據(jù)或者一個數(shù)組時,由child指針指向該條新鏈表。
3.JSON數(shù)據(jù)封裝
封裝方法
封裝JSON數(shù)據(jù)的過程,其實就是創(chuàng)建鏈表和向鏈表中添加節(jié)點的過程。
首先來講述一下鏈表中的一些術(shù)語:
- 頭指針:指向鏈表頭結(jié)點的指針;
- 頭結(jié)點:不存放有效數(shù)據(jù),方便鏈表操作;
- 首節(jié)點:第一個存放有效數(shù)據(jù)的節(jié)點;
- 尾節(jié)點:最后一個存放有效數(shù)據(jù)的節(jié)點;
明白了這幾個概念之后,我們開始講述創(chuàng)建一段完整的JSON數(shù)據(jù),即如何創(chuàng)建一條完整的鏈表。
① 創(chuàng)建頭指針:
cJSON* cjson_test = NULL;② 創(chuàng)建頭結(jié)點,并將頭指針指向頭結(jié)點:
cjson_test = cJSON_CreateObject();③ 盡情的向鏈表中添加節(jié)點:
cJSON_AddNullToObject(cJSON * const object, const char * const name);cJSON_AddTrueToObject(cJSON * const object, const char * const name);cJSON_AddFalseToObject(cJSON * const object, const char * const name);cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);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數(shù)據(jù)
上面講述,一段完整的JSON數(shù)據(jù)就是一條長長的鏈表,那么,如何打印出這段JSON數(shù)據(jù)呢?
cJSON提供了一個API,可以將整條鏈表中存放的JSON信息輸出到一個字符串中:
(char *) cJSON_Print(const cJSON *item);使用的時候,只需要接收該函數(shù)返回的指針地址即可。
封裝數(shù)據(jù)和打印數(shù)據(jù)示例
單純的講述方法還不夠,下面用一個例子來說明,封裝出開頭給出的那段JSON數(shù)據(jù):
#include <stdio.h> #include "cJSON.h"int main(void) {cJSON* cjson_test = NULL;cJSON* cjson_address = NULL;cJSON* cjson_skill = NULL;char* str = NULL;/* 創(chuàng)建一個JSON數(shù)據(jù)對象(鏈表頭結(jié)點) */cjson_test = cJSON_CreateObject();/* 添加一條字符串類型的JSON數(shù)據(jù)(添加一個鏈表節(jié)點) */cJSON_AddStringToObject(cjson_test, "name", "mculover666");/* 添加一條整數(shù)類型的JSON數(shù)據(jù)(添加一個鏈表節(jié)點) */cJSON_AddNumberToObject(cjson_test, "age", 22);/* 添加一條浮點類型的JSON數(shù)據(jù)(添加一個鏈表節(jié)點) */cJSON_AddNumberToObject(cjson_test, "weight", 55.5);/* 添加一個嵌套的JSON數(shù)據(jù)(添加一個鏈表節(jié)點) */cjson_address = cJSON_CreateObject();cJSON_AddStringToObject(cjson_address, "country", "China");cJSON_AddNumberToObject(cjson_address, "zip-code", 111111);cJSON_AddItemToObject(cjson_test, "address", cjson_address);/* 添加一個數(shù)組類型的JSON數(shù)據(jù)(添加一個鏈表節(jié)點) */cjson_skill = cJSON_CreateArray();cJSON_AddItemToArray(cjson_skill, cJSON_CreateString( "C" ));cJSON_AddItemToArray(cjson_skill, cJSON_CreateString( "Java" ));cJSON_AddItemToArray(cjson_skill, cJSON_CreateString( "Python" ));cJSON_AddItemToObject(cjson_test, "skill", cjson_skill);/* 添加一個值為 False 的布爾類型的JSON數(shù)據(jù)(添加一個鏈表節(jié)點) */cJSON_AddFalseToObject(cjson_test, "student");/* 打印JSON對象(整條鏈表)的所有數(shù)據(jù) */str = cJSON_Print(cjson_test);printf("%s\n", str);return 0; }編譯運行:
gcc cJSON.c example1.c -o example1實驗結(jié)果如圖:
該JSON數(shù)據(jù)鏈表的關(guān)系如圖:
4.cJSON數(shù)據(jù)解析
解析方法
解析JSON數(shù)據(jù)的過程,其實就是剝離一個一個鏈表節(jié)點(鍵值對)的過程。
解析方法如下:
① 創(chuàng)建鏈表頭指針:
cJSON* cjson_test = NULL;② 解析整段JSON數(shù)據(jù),并將鏈表頭結(jié)點地址返回,賦值給頭指針:
解析整段數(shù)據(jù)使用的API只有一個:
③ 根據(jù)鍵值對的名稱從鏈表中取出對應(yīng)的值,返回該鍵值對(鏈表節(jié)點)的地址
(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);④ 如果JSON數(shù)據(jù)的值是數(shù)組,使用下面的兩個API提取數(shù)據(jù):
(int) cJSON_GetArraySize(const cJSON *array); (cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);解析示例
下面用一個例子來說明如何解析出開頭給出的那段JSON數(shù)據(jù):
#include <stdio.h> #include "cJSON.h"char *message = //json格式數(shù)據(jù),需要顯示引號,都需要用\轉(zhuǎn)義 "{ \\"name\":\"mculover666\", \\"age\": 22, \\"weight\": 55.5, \\"address\": \{ \\"country\": \"China\",\\"zip-code\": 111111\}, \\"skill\": [\"c\", \"Java\", \"Python\"],\\"student\": false \ }";int main(void) {cJSON* cjson_test = NULL;cJSON* cjson_name = NULL;cJSON* cjson_age = NULL;cJSON* cjson_weight = NULL;cJSON* cjson_address = NULL;cJSON* cjson_address_country = NULL;cJSON* cjson_address_zipcode = NULL;cJSON* cjson_skill = NULL;cJSON* cjson_student = NULL;int skill_array_size = 0, i = 0;cJSON* cjson_skill_item = NULL;/* 解析整段JSO數(shù)據(jù) */cjson_test = cJSON_Parse(message);if(cjson_test == NULL){printf("parse fail.\n");return -1;}/* 依次根據(jù)名稱提取JSON數(shù)據(jù)(鍵值對) */cjson_name = cJSON_GetObjectItem(cjson_test, "name");cjson_age = cJSON_GetObjectItem(cjson_test, "age");cjson_weight = cJSON_GetObjectItem(cjson_test, "weight");printf("name: %s\n", cjson_name->valuestring);printf("age:%d\n", cjson_age->valueint);printf("weight:%.1f\n", cjson_weight->valuedouble);/* 解析嵌套json數(shù)據(jù) */cjson_address = cJSON_GetObjectItem(cjson_test, "address");cjson_address_country = cJSON_GetObjectItem(cjson_address, "country");cjson_address_zipcode = cJSON_GetObjectItem(cjson_address, "zip-code");printf("address-country:%s\naddress-zipcode:%d\n", cjson_address_country->valuestring, cjson_address_zipcode->valueint);/* 解析數(shù)組 */cjson_skill = cJSON_GetObjectItem(cjson_test, "skill");skill_array_size = cJSON_GetArraySize(cjson_skill);printf("skill:[");for(i = 0; i < skill_array_size; i++){cjson_skill_item = cJSON_GetArrayItem(cjson_skill, i);printf("%s,", cjson_skill_item->valuestring);}printf("\b]\n");/* 解析布爾型數(shù)據(jù) */cjson_student = cJSON_GetObjectItem(cjson_test, "student");if(cjson_student->valueint == 0){printf("student: false\n");}else{printf("student:error\n");}return 0; }編譯:
gcc cJSON.c example2.c -o example2運行結(jié)果如圖:
注意事項
在本示例中,因為我提前知道數(shù)據(jù)的類型,比如字符型或者浮點型,所以我直接使用指針指向?qū)?yīng)的數(shù)據(jù)域提取,在實際使用時,如果提前不確定數(shù)據(jù)類型,應(yīng)該先判斷type的值,確定數(shù)據(jù)類型,再從對應(yīng)的數(shù)據(jù)域中提取數(shù)據(jù)。
5.cJSON使用過程中的內(nèi)存問題
內(nèi)存及時釋放
cJSON的所有操作都是基于鏈表的,所以cJSON在使用過程中大量的使用malloc從堆中分配動態(tài)內(nèi)存的,所以在使用完之后,應(yīng)當及時調(diào)用下面的函數(shù),清空cJSON指針所指向的內(nèi)存,該函數(shù)也可用于刪除某一條數(shù)據(jù):
(void) cJSON_Delete(cJSON *item);注意:該函數(shù)刪除一條JSON數(shù)據(jù)時,如果有嵌套,會連帶刪除。
內(nèi)存鉤子
cJSON在支持自定義malloc函數(shù)和free函數(shù),方法如下:
① 使用cJSON_Hooks來連接自定義malloc函數(shù)和free函數(shù):
typedef struct cJSON_Hooks {/* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */void *(CJSON_CDECL *malloc_fn)(size_t sz);void (CJSON_CDECL *free_fn)(void *ptr); } cJSON_Hooks;② 初始化鉤子cJSON_Hooks
(void) cJSON_InitHooks(cJSON_Hooks* hooks);cJSON在STM32移植和使用
參照:物聯(lián)網(wǎng)項目設(shè)計(四)cJSON 在 STM32 移植和使用
作者:張競豪
地址:https://blog.csdn.net/weixin_42487906/article/details/104542529
概要
JSON格式是互聯(lián)網(wǎng)通訊過程中常用的格式。包括MQTT協(xié)議也不例外,MQTT協(xié)議的數(shù)據(jù)收發(fā)都是使用JSON格式完成的。在使用MQTT協(xié)議進行通訊之前,我們需要掌握在c語言環(huán)境下JSON格式數(shù)據(jù)的操作。對JSON數(shù)據(jù)的操作我們使用了cJSON開源庫。在使用的過程中,也遇到了不少問題(見后文),為此我查閱了很多資料,最后找到比較簡單的方法解決了這個問題。
本篇介紹如何生成cJSON格式數(shù)據(jù),并轉(zhuǎn)化成字符串,通過串口發(fā)送到上位機。
硬件準備
使用STM32主控的開發(fā)板或相關(guān)硬件設(shè)備
軟件準備
上位機串口助手
實際操作步驟
1.使用CubeMx建立一個簡單的工程,只需要配置串口,但一定要注意,配置堆棧大小的時候一定要配置的大一點,因為cJSON分配內(nèi)存的時候會占用不少內(nèi)存空間,內(nèi)存分配是單片機移植cJSON遇到的最常見的問題,如果內(nèi)存分配的不對,很容易就導致生成的JSON字符串里面什么都沒有。網(wǎng)上很多解決方案是自行編寫內(nèi)存管理函數(shù),也就是malloc和free,這種方法太麻煩而且可移植性不高,最后我選擇了使用增大堆棧的方式。
2.從github上獲取cJSON的源碼并解壓,如下圖
我們只需要其中的cJSON.c和cJSON.h,并把他們復(fù)制到工程中。
3.實際編寫代碼部分,我們需要生成一個JSON格式的字符串如下
實際編寫代碼部分,我們要注意的是,在使用cJSON之后,要及時將其刪除,其中刪除父對象也會自動迭代刪除子對象
下面是初始化部分,完成生成JSON格式數(shù)據(jù),并將其轉(zhuǎn)化為字符串格式數(shù)據(jù),最后刪除
char *buffer;cJSON* cjson_test = NULL;cJSON* cjson_address = NULL;cJSON* cjson_info = NULL;cjson_test = cJSON_CreateObject();////cJSON_AddStringToObject(cjson_test,"name","Alpha");cjson_address = cJSON_CreateObject();cJSON_AddStringToObject(cjson_address,"conutry","China");cJSON_AddStringToObject(cjson_address,"city","Luoyang");cJSON_AddItemToObject(cjson_test,"address",cjson_address);cjson_info = cJSON_CreateObject();cJSON_AddNumberToObject(cjson_info,"age",21);cJSON_AddNumberToObject(cjson_info,"weight",66);cJSON_AddItemToObject(cjson_test,"info",cjson_info);buffer = cJSON_Print(cjson_test);cJSON_Delete(cjson_test);下面是發(fā)送字符串到上位機部分
/* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */HAL_Delay(1000);u1_printf("You Json Is:%s\r\n",buffer);}/* USER CODE END 3 */ }4,編譯代碼并在上位機中觀察現(xiàn)象
完成了所需要的功能
總結(jié)
本部分內(nèi)容完成了cJSON的移植和JSON格式的初步使用,下一階段準備使用paho 的開源mqtt庫鏈接百度云天工,并且使用paho自帶的ping函數(shù),完成stm32鏈接百度云天工后的連接檢測。
快速生成JSON數(shù)據(jù)(sprintf)和解析JSON數(shù)據(jù)(strstr)技巧
參照:騷操作!快速創(chuàng)建JSON數(shù)據(jù)和解析JSON數(shù)據(jù)
作者:Mculover666
地址:https://mculover666.blog.csdn.net/article/details/103794279?spm=1001.2014.3001.5502
樂鑫云平臺在對接的時候,可以使用tcp socket發(fā)送和接收json數(shù)據(jù)進行交互,之前專門寫了一篇文章講述cJSON的使用,然而,看了樂鑫給的官方代碼后,我只能說,突如其來的騷,閃了**的腰!
1. 生成JSON數(shù)據(jù)
核心思想:構(gòu)造字符串!
這里我拿設(shè)備激活舉個例子,設(shè)備激活的數(shù)據(jù)格式如下:
{"path": "/v1/device/activate/", "method": "POST", "meta": {"Authorization": "token HERE_IS_THE_MASTER_DEVICE_KEY"}, "body": {"encrypt_method": "PLAIN", "bssid": ":bssid", "token": ":token"}}其中:
- HERE_IS_THE_MASTER_DEVICE_KEY:填寫之前獲取的秘鑰;
- bssid:填寫設(shè)備MAC地址;
- token :隨機值即可,可以留空;
這樣的一條數(shù)據(jù),如果使用cJSON的函數(shù)一條一條創(chuàng)建,不僅代碼量大,而且內(nèi)存占用量也極大,接下來向大家介紹一個騷操作,看完后,我相信你會點贊的!
首先直接使用宏定義將整段JSON字符串給出:
#define ACTIVE_DATA \ "{\n\"path\": \"/v1/device/activate/\",\n\ \"method\": \"POST\",\n\ \"meta\":\ {\"Authorization\": \"token %s\"},\n\ \"body\":\ {\"encrypt_method\": \"PLAIN\",\ \"bssid\": \"%s\",\ \"token\": \":\"}\n}\n"然后一行命令即可構(gòu)造出將其中兩個需要用戶給出的值定義:
#define TOKEN "ef97b306620d5e9de19d7b2131742b152b2e94d0" #define BSSID "dc:4f:22:5e:90:b7"最后一行代碼解決問題,簡潔到爆炸:使用拼接函數(shù)
sprintf(active_data, ACTIVE_DATA, TOKEN, BSSID);整段程序如下:
#include <stdio.h> #include <stdlib.h>#define ACTIVE_DATA \ "{\n\"path\": \"/v1/device/activate/\",\n\ \"method\": \"POST\",\n\ \"meta\":\ {\"Authorization\": \"token %s\"},\n\ \"body\":\ {\"encrypt_method\": \"PLAIN\",\ \"bssid\": \"%s\",\ \"token\": \":\"}\n}\n"#define TOKEN "ef97b306620d5e9de19d7b2131742b152b2e94d0" #define BSSID "dc:4f:22:5e:90:b7"int main(void) {char* active_data = malloc(300);sprintf(active_data, ACTIVE_DATA, TOKEN, BSSID);printf(active_data);return 0; }編譯運行:
2. 解析JSON數(shù)據(jù)
"deliver_to_device": true, "get": "action": "LED_ON", "meta": "Authorization": "token 8668c64f40d172be31eb4f12cbcaf4e57c9e1e3c", "Time-Zone": "Asia/Shanghai", "method": "GET", "nonce": 587250592, "path": "/v1/device/rpc/"其中想要的數(shù)據(jù)只有:
所以,為什么要浪費精力,浪費代碼,浪費內(nèi)存去用cJSON一條一條解析數(shù)據(jù),而且解析出來有用的值只有“LED_ON”,所以直接使用C庫函數(shù)strstr()判斷這段字符串中有沒有“LED_ON”就ok啦~
編譯運行:
Keil環(huán)境下Jansson解析庫的使用
摘自:Keil環(huán)境下Jansson解析庫的使用——基于STM32F103
地址:https://blog.csdn.net/whik1194/article/details/106872260
摘自:Keil環(huán)境下使用Jansson構(gòu)建JSON——基于STM32F103
地址:https://blog.csdn.net/whik1194/article/details/108596840?spm=1001.2014.3001.5502
對于嵌入式開發(fā),比較常用的就是cJSON解析庫了,但是使用這個庫對內(nèi)存開銷比較大,需要大量使用malloc和free進行動態(tài)內(nèi)存分配和釋放。對于單片機這種資源短缺的芯片來說,簡直是太吃力了。使用STM32等單片機,不能解析多層JSON時,還需要調(diào)大堆棧。
今天來了解一下一個和cJSON非常類似的解析庫——Jansson的使用,支持解析和構(gòu)建JSON字符串,不需要動態(tài)分配內(nèi)存,使用方法和cJSON幾乎一樣。
總結(jié)
以上是生活随笔為你收集整理的JSON数据格式解析库(cJSON、Jansson)的使用在STM32上移植和使用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数学建模论文写作
- 下一篇: 下滑加载更多js_专治:卫生间免砸砖,房