徐锦洪 发表于 2024-10-31 11:26:06

STM32单片机与cJSON:构建并解析JSON数据

在现代嵌入式体系开发中,数据交换和通信变得日益重要尤其是在单片机设备接入云端的过程中常常会用到构建息争析JSON格式。而且JSON(JavaScript Object Notation)作为一种轻量级的数据交换格式,因其简洁、易于阅读和编写,以及跨平台兼容性的上风,在嵌入式体系中得到了广泛应用。本文将详细介绍如安在STM32微控制器上使用cJSON库来生成息争析JSON数据,为开发者们提供一个实用的指南。
一、准备工作

1. cJSON库简介

cJSON是一个轻量级的、易于使用的C语言库,用于解析和生成JSON数据。它只包含两个文件:cJSON.c和cJSON.h,非常适合资源受限的嵌入式体系。cJSON的设计目标是简单、快速和高效,因此它非常适合在STM32如许的微控制器上运行。
2. 硬件环境



[*]STM32微控制器(本次接纳STM32F103RCT6)
[*]串口通信模块
3. 软件环境



[*]Keil uVision IDE(或其他支持STM32的IDE)
[*]STM32CubeMX(可选,用于设置硬件)
[*]cJSON库文件(cJSON.c和cJSON.h)
[*]工程接纳之前制作的串口模版STM32-HAL库串口空闲中断DMA汲取数据Demo-CSDN博客
二、实现步骤

1. 导入添加cJSON库

添加cjson.c和cjson.h文件到工程中,工程中相关文件放在工程目录下User文件夹下。https://i-blog.csdnimg.cn/direct/b82f40c9118b485683e2f4704164673d.png
在json文件中可以看到已经封装好大量函数,在使用过程中只需要包含cjson.h文件便可以调用构建息争析json的函数
2. 创建JSON字符串

2.1. 根本用法

在STM32上创建一个JSON对象并添加数据非常简单。首先,需要创建一个cJSON对象,然后使用cJSON提供的API函数向此中添加数据。如下代码所示能够在json对象中分别添加字符串string类型、数字以及bool类型:
cJSON *root = cJSON_CreateObject();
cJSON_AddStringToObject(root, "name", "STM32");
cJSON_AddNumberToObject(root, "age",5);
cJSON_AddBoolToObject(root, "is_running", cJSON_True); 创建好JSON对象后,需要将其转换为字符串以便于传输或存储。cJSON提供了cJSON_Print函数来完成这一使命。
char *json_string = cJSON_Print(root);
printf("%s\n", json_string); 现在就可以输出一个完备的json字符串:
{
    "name": "stm32",
    "age": 5,
    "is_running": true
} 在实际使用过程中需要考虑到单片机内存空间问题,假如创建失败大概会导致内存泄漏等问题,所以在单片使用时需要增长相关判定和错误处置惩罚
2.2. 单片机中cJSON的用法



[*]构建目标
下方json字符串为华为云IoTDA中设备上报属性的格式,在json字符串中嵌套json字符串
{
    "name": "environment",
    "id": 1,
    "params": {
      "temp": 23.5,
      "humi": 50,
      "o2": 21
    }
}

[*]函数实现
在实现前需要确保stm32单片机的堆空间充足大,保证后续json构建能够乐成。https://i-blog.csdnimg.cn/direct/fe64f9e77e184f15aa23d4ae5df1c335.png
如下代码能够实现了 cJSON 对象的创建、添补、转换为字符串、打印以及最终的内存开释。
void getJsonData(void)
{
        cJSON *pOrder = cJSON_CreateObject();
    cJSON *params = cJSON_CreateObject();
    char *json_body = NULL;
       
        if (pOrder == NULL || params == NULL)
    {
      printf("Creat ENV JSONobj Err\n");
      cJSON_Delete(pOrder);
      cJSON_Delete(params);
      return;
    }
       
        // 添加参数到params对象,并检查每个调用的返回值
    if (!cJSON_AddNumberToObject(params,"temp",23.5)||
      !cJSON_AddNumberToObject(params,"humi",50)        ||
      !cJSON_AddNumberToObject(params,"o2",21))
    {
      printf("Add ENV data to JSONobj Err\n");
      cJSON_Delete(pOrder);
      cJSON_Delete(params);
      return;
    }
       
        // 添加params到pOrder对象
    cJSON_AddStringToObject(pOrder, "name", "environment");
    cJSON_AddNumberToObject(pOrder, "id", 1);
    cJSON_AddItemToObject(pOrder, "params", params);
       
        // 转换JSON对象为字符串
    json_body = cJSON_PrintUnformatted(pOrder);
       
    if (json_body == NULL)
    {
      printf("Print ENV JSON Err\n");
      cJSON_Delete(pOrder);
      return;
    }
       
        // 打印并发送JSON字符串
    printf("ENV json: %s \r\n", json_body);
       
        cJSON_free(json_body);
        cJSON_Delete(pOrder);
}

[*]分析

[*]错误检查:在创建 cJSON 对象后,检查 pOrder 和 params 是否为 NULL。因为内存分配大概会失败,尤其是在资源受限的环境中。
[*]参数添加:使用 cJSON_AddNumberToObject 添加了数字类型的参数到 params 对象中,并检查了每个调用的返回值,能够确保数据正确添加到 JSON 对象中的好方法。
[*]对象嵌套:使用 cJSON_AddItemToObject 将 params 对象作为子项添加到了 pOrder 对象中。params 的所有权转移给了 pOrder,因此在后续调用cJSON_Delete删除时不需要在之后单独删除 params。
[*]字符串转换:使用 cJSON_PrintUnformatted 将 pOrder 对象转换为了一个未格式化的字符串,并检查了转换是否乐成。
[*]内存开释:最后开释了转换后的字符串 json_body 所占用的内存,以及整个 pOrder 对象及其所有子对象所占用的内存。
3. 解析JSON字符串

3.1. 根本用法

汲取到的JSON字符串可以使用cJSON_Parse函数举行解析。解析后的结果是一个cJSON对象,你可以通过cJSON_GetObjectItem等函数来访问此中的数据。
cJSON *parsed_json = cJSON_Parse(json_string);
if (parsed_json == NULL) {
    // 解析失败处理
    printf("Parse error\n");
} else {
    cJSON *name = cJSON_GetObjectItem(parsed_json, "name");
    if (name != NULL) {
      printf("Name: %s\n", name->valuestring);
    }
    // 继续解析其他数据...
    cJSON_Delete(parsed_json);
} 3.2. 单片机中的用法

在stm32单片机中需要先判定汲取到的字符串是否完备,并对一些错误举行防范。解析将以华为云IoTDA中应用侧下发的控制下令为例,其下令格式如下:
{
    "object_device_id": 123,
    "command_name": "LED",
    "service_id": "WaterMeter",
    "paras": {
      "sw": true,
      "val": 50
    }
} 此中paras中的sw代表开关,val代表亮度值,解析代码如下,第一部分先查找汲取到的数据中完备的json字符串,第二部分分配内存空间复制生存json数据,第三部分解析json数据
int8_t Parse_MqttCmd(uint8_t *data)
{
        // 寻找JSON数据的开始位置
        const char *json_start = strstr((char *)data, "{");
        if (json_start == NULL)
        {
                printf("JSON data not found in the received string.\n");
                return -1;
        }
        size_t json_length = strlen(json_start);
       
        // 分配内存并复制JSON数据
        char *json_data = (char *)malloc(json_length + 1);
        if (json_data == NULL) {
                printf("Memory allocation failed.\n");
                return -1;
        }
        strncpy(json_data, json_start, json_length);
        json_data = '\0'; // 添加null终止符

        //解析JSON数据
        cJSON *root = cJSON_Parse(json_data);
        if (root == NULL)
        {
                printf("Failed to parse JSON data.\n");
                cJSON_free(json_data);
                return -1;
        }
        // ... 在这里处理JSON数据 ...
               
        // 获取并打印"command_name"字段的值
        cJSON *name = cJSON_GetObjectItemCaseSensitive(root, "command_name");

        //判断"command_name"字段的值选择控制类型
        if (name && cJSON_IsString(name) && (name->valuestring != NULL))
        {
                char * command_name = name->valuestring;
                printf("Name: %s \r\n", command_name);
               
                //灯光控制命令
                if(strstr(name->valuestring,"LED"))
                {
                        cJSON *paras = cJSON_GetObjectItemCaseSensitive(root, "paras");/*获取obj中的paras的json*/
                        if (paras && cJSON_IsObject(paras))
                        {
                                cJSON *sw_item = cJSON_GetObjectItemCaseSensitive(paras, "sw");/*开关状态*/
                                if (sw_item != NULL && cJSON_IsBool(sw_item))
                                {
                                        int sw_value = sw_item->valueint; // cJSON使用int来表示bool值
                                        printf("sw: %d \r\n", sw_value); // 输出sw的值,1代表true,0代表false
                                }else
                                {
                                        printf("Failed to get 'sw' value or it's not a boolean.\r\n");
                                }
                                cJSON *val_item = cJSON_GetObjectItemCaseSensitive(paras, "val");
                                if (val_item != NULL && cJSON_IsNumber(val_item))
                                {
                                        int val = val_item->valueint; // cJSON使用int来表示bool值
                                        printf("val: %d \r\n", val); // 输出sw的值,1代表true,0代表false
                                }else
                                {
                                        printf("Failed to get 'val' value or it's not a number. \r\n");
                                }
                        }                                       
                }
        }
        // 释放资源
        cJSON_Delete(root);
        cJSON_free(json_data);
        return 1;
}

[*]分析

[*]字段存在性和类型检查:通过 cJSON_GetObjectItemCaseSensitive 和相应的类型检查函数来确保字段存在且类型正确。
[*]内存分配和开释:分配内存来存储 JSON 字符串,并在处置惩罚完 JSON 数据后开释了这块内存也开释了 cJSON 对象。
[*]错误处置惩罚:在JSON 解析失败和内存分配失败添加了错误提示。
4. 留意事项


[*]内存管理:STM32的内存资源有限,特殊是在使用cJSON库时,需要确保有充足的堆内存来存储JSON数据,需要调解STM32的堆栈大小。
[*]错误处置惩罚:在解析JSON字符串时,一定要检查返回值,确保解析没有失败。
三、cJSON库各类函数解析

1. 解析和创建函数



[*]cJSON_Parse(): 解析一个JSON格式的字符串,并返回一个cJSON指针,指向解析后的JSON结构的根。假如解析失败,返回NULL。
[*]cJSON_Create 系列函数(如cJSON_CreateObject(), cJSON_CreateArray(), cJSON_CreateString(), cJSON_CreateNumber(), cJSON_CreateBool(), cJSON_CreateNull()): 这些函数用于创建差别类型的cJSON对象,分别对应于JSON中的对象、数组、字符串、数字、布尔值和null。
2. 访问和修改函数



[*]cJSON_GetObjectItemCaseSensitive() 和 cJSON_GetObjectItem(): 根据键名查找JSON对象中的项。第一个函数对大小写敏感,第二个函数则不敏感。
[*]cJSON_GetArrayItem(): 根据索引获取JSON数组中的项。
[*]cJSON_AddItemToObject(), cJSON_AddItemToArray(), cJSON_AddItemReferenceToArray(), cJSON_AddItemToObjectCS(): 这些函数用于向JSON对象或数组中添加项。此中,带有CS后缀的函数对键名大小写敏感。
[*]cJSON_ReplaceItemInObjectCaseSensitive() 和 cJSON_ReplaceItemInObject(): 根据键名更换JSON对象中的项。与GetObjectItem函数类似,第一个函数对大小写敏感。
[*]cJSON_DetachItemFromObject() 和 cJSON_DetachItemFromArray(): 从JSON对象或数组中移除项,但不开释其内存。
[*]cJSON_DeleteItemFromObject() 和 cJSON_DeleteItemFromArray(): 从JSON对象或数组中移除项,并开释其内存。
3. 辅助函数



[*]cJSON_Print() 和 cJSON_PrintUnformatted(): 将cJSON结构转换回JSON格式的字符串。第一个函数格式化输出,第二个函数不格式化。
[*]cJSON_PrintBuffered(): 类似于cJSON_Print,但允许使用自界说的缓冲区。
[*]cJSON_Duplicate(): 创建一个cJSON项的深拷贝。
[*]cJSON_NumStrings 和相关宏(如cJSON_IsArray, cJSON_IsObject, cJSON_IsString, cJSON_IsNumber, cJSON_IsBool, cJSON_IsNull): 这些函数和宏用于检查cJSON项的类型。
4. 错误处置惩罚



[*]cJSON_GetErrorPtr(): 返回一个指向全局变量的指针,该变量包含解析错误时的错误信息(假如有的话)。
5. 内存管理



[*]cJSON_Delete(): 开释一个cJSON项及其所有子项占用的内存。
四、源码链接

【免费】stm32+cjson库实现json格式创建与解析资源-CSDN文库

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: STM32单片机与cJSON:构建并解析JSON数据