STM32在Keil5中利用Jansson库处理和组装JSON数据【详细版】

在Keil5中利用Jansson库处理和组装JSON数据

下载Keil.Jansson.1.0.0.pack

https://keilpack.azureedge.net/pack/Keil.Jansson.1.0.0.pack 下载完成后直接安装到keil5中即可

选择Jansson库的理由:轻量、易用、适配嵌入式

Jansson库之所以被选用,主要基于以下三大核心优势:

  • 轻量级:Jansson库设计紧凑,占用资源少,非常适合资源受限的嵌入式环境。
  • 易用性:提供简洁直观的API,使得JSON数据的解析与组装变得简单快捷。
  • 嵌入式适配:特别注重在嵌入式系统中的应用,支持多种平台,优化性能以减少资源消耗。
  • 这些特点使得Jansson库成为嵌入式系统中处理JSON数据的优选工具。

    配置Jansson

    ①:打开keil5后,点击工具栏中魔术棒右边具有四个黑点的绿色正方形

    ②:点击Data Exchange: 里面可以看到安装的拓展库,例如这里显示JSON,

                    这里将下方的Jansson勾选

    如果keil5左边项目文件中包含了这个图像和相关的jansson文件代表Jansson环境配置成功

    配置printf
     

    如果需要使用printf的话,这里放一个传送门

    或者也可以直接使用串口发送函数

    STM32基于HAL库串口printf使用和接收https://blog.csdn.net/Wang2869902214/article/details/141719519

    这里无论是不是HAL库,都可以按照上方文章说明配置,
    只需在重定向代码中更换对应的串口发送函数

    解析json格式数据代码

    这里使用STM32F103C8T6芯片作为测试硬件,软件方面需要准备一个串口调试助手,用来查看代码运行时的状态

    文章中的代码只是用来举一反三,可以根据它的编程规律和函数命名规律,可以合理的推断出解析整形还是字符串还是浮点数的转换函数
     

    这里注意一点:
    如果Json解析代码是正确的,然后解析的内容与预期不符,很可能是程序中设置的堆大小不足,
    解决方法:在启动文件中:startup_stm32f103xb.s中找到

    Heap_Size      EQU     0x200

    修改为(也可以在STM32CubeMX软件代码生成时修改)

    Heap_Size      EQU     0x800

    这里使用简单的字符串先模拟一下解析json格式的数据,提取里面每个Json对象的数据

    创建了一个字符串:

    const char *json_str = "{\"name\":\"Huan\",\"age\":19,\"city\":\"Mei Zhou\"}";

    目的:提取出这些"name","age","city"字段的数据

    完整代码1

    #include "main.h"
    #include <stdio.h>  
    #include "jansson.h"  
    
    const char *json_str = "{\"name\":\"Huan\",\"age\":19,\"city\":\"Mei Zhou\"}";
    
    typedef struct
    {
    	char name[10];
    	uint8_t age;
    	char city[10];
    }Student_TypeDef;
    
    
    
    uint8_t myJsonTest(const char *jsonString, Student_TypeDef *student)
    {
        // 解析JSON字符串  
        json_t *root;  
        json_error_t error;  
      
        root = json_loads(jsonString, 0, &error);  
        if (!root) {  
            fprintf(stderr, "Error parsing JSON: %s\r\n", error.text);  
            return 1;  
        }  
      
        // 检查JSON对象是否为有效对象  
        if (!json_is_object(root)) {  
            fprintf(stderr, "Error: JSON is not an object\r\n");  
            json_decref(root);  
            return 1;  
        }  
      
        // 提取并打印name字段  
        json_t *name_value = json_object_get(root, "name");  
        if (json_is_string(name_value)) {  
    		strcpy(student->name, json_string_value(name_value));
        } else {  
            printf("Name field is not a string\r\n");  
        }  
      
        // 提取并打印age字段  
        json_t *age_value = json_object_get(root, "age");  
        if (json_is_integer(age_value)) {  
    		student->age = json_integer_value(age_value);
        } else {  
            printf("Age field is not an integer\r\n");  
        }  
      
        // 提取并打印city字段  
        json_t *city_value = json_object_get(root, "city");  
        if (json_is_string(city_value)) {  
    		strcpy(student->city, json_string_value(city_value)); 
        } else {  
            printf("City field is not a string\r\n");  
        }  
      
        // 释放JSON对象  
        json_decref(root);  
      
        return 0;  
    }
    
    int main(void)
    {
     
      HAL_Init();
      SystemClock_Config();
      MX_GPIO_Init();
      MX_DMA_Init();
      MX_USART1_UART_Init();
    	
    
      while (1)
      {
    	Student_TypeDef student = {{0}, 0, {0}};
    	printf("\r\n%s\r\n",json_str);
    	myJsonTest(json_str, &student);
    	printf("name:%s\r\n",student.name);
    	printf("age:%d\r\n",student.age);
    	printf("city:%s\r\n",student.city);
    	HAL_Delay(5000);  
      }
    }

    代码1运行效果

    其他的数据类型转换方法就举一反三即可

    这里注意一点:
    如果Json解析代码是正确的,然后解析的内容与预期不符,很可能是程序中设置的堆大小不足,
    解决方法:在启动文件中:startup_stm32f103xb.s中找到

    Heap_Size      EQU     0x200

    修改为(也可以在STM32CubeMX软件代码生成时修改)

    Heap_Size      EQU     0x800

    注意事项:上文提到加堆设置,是因为这里json初始化使用的是动态分配空间(malloc),如果堆设置范围太小,那么这里json解析时分配不了所需要的空间,会导致解析失败,包括后面要提到的组装json

    json_loads(jsonString, 0, &error);  //内部处理:动态开辟空间

    既然是需要动态分配空间,那么在使用结束后也是需要释放空间的,否则运行几次程序就会异常

     json_decref(root);  //内部处理:释放空间

    也正是因为这个代码的使用他需要开辟和释放空间,就说明在这个内存的生命周期结束后,里面所有的返回字符串指针全部会被清空,涉及到字符串的情况下,需要使用拷贝函数,将字符串拷贝到自己生命周期更长的变量中,例如上方的Student_TypeDef结构体

    如果需要解析一个多重json对象,例如下方代码

    const char *json_str = "{\"id\": \"123\",\"params\":{\"name\":{\"value\":\"Huan\"},\"age\":{\"value\":19},\"city\":{\"value\":\"Mei Zhou\"}}}"; 
    
    //原型如下:
    {  
      "id": "123",  
      "params": {  
        "name": {  
          "value": "Huan"  
        },  
        "age": {  
          "value": 19  
        },  
        "city": {  
          "value": "Mei Zhou"  
        }  
      }  
    }

    完整代码2

    #include "main.h"
    #include <stdio.h>  
    #include "jansson.h"  
    
    const char *json_str = "{\"id\": \"123\",\"params\":{\"name\":{\"value\":\"Huan\"},\"age\":{\"value\":19},\"city\":{\"value\":\"Mei Zhou\"}}}"; 
    typedef struct
    {
    	char city[10];
    	char name[10];
    	char id[10];
    	uint8_t age;
    	
    }Student_TypeDef;
    
    
    
    uint8_t myJsonTest(const char *jsonString, Student_TypeDef *student)
    {
    json_t *root;  
        json_error_t error;  
      
        root = json_loads(jsonString, 0, &error);  
        if (!root) {  
            fprintf(stderr, "Error parsing JSON: %s\r\n", error.text);  
            return 1;  
        }  
      
        if (!json_is_object(root)) {  
            fprintf(stderr, "Error: JSON is not an object\r\n");  
            json_decref(root);  
            return 1;  
        }  
      
    	// 提取id的值  
        json_t *id_value = json_object_get(root, "id");  
        if (json_is_string(id_value)) {  
    		//id = (char *)json_string_value(id_value);
    		strcpy(student->id, (char *)json_string_value(id_value));
        } else {  
            printf("id is not a string\r\n");  
        } 
    	
        json_t *params = json_object_get(root, "params");  
        if (json_is_object(params)) {  
            // 提取并打印name字段  
            json_t *name_obj = json_object_get(params, "name");  
            if (json_is_object(name_obj)) {  
                json_t *name_value = json_object_get(name_obj, "value");  
                if (json_is_string(name_value)) {  
                    strncpy(student->name, json_string_value(name_value), sizeof(student->name) - 1);  
                } else {  
                    printf("Name value is not a string\r\n");  
                }  
            } else {  
                printf("Name field is not an object\r\n");  
            }  
      
            // 提取并打印age字段  
            json_t *age_obj = json_object_get(params, "age");  
            if (json_is_object(age_obj)) {  
                json_t *age_value = json_object_get(age_obj, "value");  
                if (json_is_integer(age_value)) {  
                    student->age = (uint8_t)json_integer_value(age_value);  
                } else {  
                    printf("Age value is not an integer\r\n");  
                }  
            } else {  
                printf("Age field is not an object\r\n");  
            }  
      
            // 提取并打印city字段  
            json_t *city_obj = json_object_get(params, "city");  
            if (json_is_object(city_obj)) {  
                json_t *city_value = json_object_get(city_obj, "value");  
                if (json_is_string(city_value)) {  
                    strncpy(student->city, json_string_value(city_value), sizeof(student->city) - 1);  
                } else {  
                    printf("City value is not a string\r\n");  
                }  
            } else {  
                printf("City field is not an object\r\n");  
            }  
        } else {  
            printf("Params field is not an object\r\n");  
        }  
      
        json_decref(root);  
        return 0;   
    }
    
    int main(void)
    {
     
      HAL_Init();
      SystemClock_Config();
      MX_GPIO_Init();
      MX_DMA_Init();
      MX_USART1_UART_Init();
    	
    	printf("Hello World!\r\n");
    
      while (1)
      {
    	Student_TypeDef student = {{0}, {0}, {0}, 0};
    	myJsonTest(json_str, &student);
    	printf("id:%s\r\n",student.id);
    	printf("name:%s\r\n",student.name);
    	printf("age:%d\r\n",student.age);
    	printf("city:%s\r\n",student.city);
    	HAL_Delay(5000);  
      }
    }

    代码2运行效果:

     

     这里注意一点:
    如果Json解析代码是正确的,然后解析的内容与预期不符,很可能是程序中设置的堆大小不足,
    解决方法:在启动文件中:startup_stm32f103xb.s中找到

    Heap_Size      EQU     0x200

    修改为(也可以在STM32CubeMX软件代码生成时修改)

    Heap_Size      EQU     0x800

    组装json格式数据代码

     这里注意一点:
    如果Json组装代码是正确的,然后组装的内容与预期不符(可能时乱码也可能是空),很可能是程序中设置的堆大小不足,
    解决方法:在启动文件中:startup_stm32f103xb.s中找到

    Heap_Size      EQU     0x200

    修改为(也可以在STM32CubeMX软件代码生成时修改)

    Heap_Size      EQU     0x800

    这里预计组装一个json格式的数据:

    {  "name": "Huan",  "age": 19,  "city": "Mei Zhou"}

    对于非常简单且固定的JSON格式数据,可以考虑直接使用snprintf或类似的字符串格式化函数来生成JSON字符串。

    示例:

    #include <stdio.h>  
    #include <string.h>  
      
    // 定义全局缓冲区  
    #define BUFFER_SIZE 128  
    char global_buffer[BUFFER_SIZE];  
      
    // 函数声明  
    void create_json_string(const char* name, int age, const char* city);  
      
    // 主函数  
    int main(void)  
    {  
        create_json_string("Huan", 19, "Mei Zhou");  
        printf("Created JSON: %s\n", global_buffer);  
        return 0;  
    }  
      
    // 创建JSON字符串的函数  
    void create_json_string(const char* name, int age, const char* city)  
    {  
        // 使用snprintf将JSON字符串写入全局缓冲区  
        snprintf(global_buffer, BUFFER_SIZE, "{\"name\": \"%s\", \"age\": %d, \"city\": \"%s\"}", name, age, city);  
    }

    完整代码1

    #include "main.h"  
    #include <stdio.h>  
    #include "jansson.h"  
    #include <string.h> // 添加对字符串操作的支持  
      
    // 函数声明  
    char* create_json_string(const char* name, uint8_t age, const char* city);  
      
    // 主函数  
    int main(void)  
    {  
        HAL_Init();  
        SystemClock_Config();  
        MX_GPIO_Init();  
        MX_DMA_Init();  
        MX_USART1_UART_Init();  
      
        while (1)  
        {  
            // 创建 JSON 字符串  
            char* json_str = create_json_string("Huan", 19, "Mei Zhou");  
            if (json_str != NULL) {  
                // 打印 JSON 字符串  
                printf("\r\nCreated JSON: %s\r\n", json_str);  
                // 释放 JSON 字符串内存  
                free(json_str); // 这里需要释放内存,因为json_dumps分配了新内存  
            }  
            HAL_Delay(5000);  
        }  
    }  
      
    // 创建 JSON 字符串的函数  
    char* create_json_string(const char* name, uint8_t age, const char* city)  
    {  
        // 创建 JSON 对象  
        json_t *root = json_object();  
        if (!root) {  
            fprintf(stderr, "Unable to create JSON object\r\n");  
            return NULL;  
        }  
      
        // 添加 name 字段  
        if (json_object_set_new(root, "name", json_string(name)) != 0) {  
            fprintf(stderr, "Unable to set name field\r\n");  
            json_decref(root);  
            return NULL;  
        }  
      
        // 添加 age 字段  
        if (json_object_set_new(root, "age", json_integer(age)) != 0) {  
            fprintf(stderr, "Unable to set age field\r\n");  
            json_decref(root);  
            return NULL;  
        }  
      
        // 添加 city 字段  
        if (json_object_set_new(root, "city", json_string(city)) != 0) {  
            fprintf(stderr, "Unable to set city field\r\n");  
            json_decref(root);  
            return NULL;  
        }  
      
        // 将 JSON 对象转换为字符串  
        char *jsonString = json_dumps(root, JSON_INDENT(2));  
        if (!jsonString) {  
            fprintf(stderr, "Unable to dump JSON string\r\n");  
            json_decref(root);  
            return NULL;  
        }  
      
        // 释放 JSON 对象  
        json_decref(root);  
        return jsonString;  
    }

    代码1运行效果:

    完整代码2

    如果像组装多重嵌套json对象的话,例如:
     

    {  
      "id": "123",  
      "params": {  
        "name": {  
          "value": "Huan"  
        },  
        "age": {  
          "value": 19  
        },  
        "city": {  
          "value": "Mei Zhou"  
        }  
      }  
    }
    
    #include "main.h"  
    #include <stdio.h>  
    #include "jansson.h"  
      
    typedef struct {  
        char city[10];  
        char name[10];  
        char id[10];  
        uint8_t age;  
    } Student_TypeDef;  
      
    char* create_json_string(const Student_TypeDef *student) {  
        json_t *root, *params, *name_obj, *age_obj, *city_obj;  
        char *json_str;  
      
        // 创建根对象  
        root = json_object();  
        if (!root) {  
            fprintf(stderr, "Failed to create root JSON object\r\n");  
            return NULL;  
        }  
      
        // 设置 id 字段  
        json_object_set_new(root, "id", json_string(student->id));  
      
        // 创建 params 对象  
        params = json_object();  
        if (!params) {  
            fprintf(stderr, "Failed to create params JSON object\r\n");  
            json_decref(root);  
            return NULL;  
        }  
      
        // 创建并设置 name 对象及其 value 字段  
        name_obj = json_object();  
        json_object_set_new(name_obj, "value", json_string(student->name));  
        json_object_set_new(params, "name", name_obj);  
      
        // 创建并设置 age 对象及其 value 字段  
        age_obj = json_object();  
        json_object_set_new(age_obj, "value", json_integer(student->age));  
        json_object_set_new(params, "age", age_obj);  
      
        // 创建并设置 city 对象及其 value 字段  
        city_obj = json_object();  
        json_object_set_new(city_obj, "value", json_string(student->city));  
        json_object_set_new(params, "city", city_obj);  
      
        // 将 params 对象添加到根对象中  
        json_object_set_new(root, "params", params);  
      
        // 将 JSON 对象转换为字符串  
        json_str = json_dumps(root, JSON_INDENT(0)); // 使用缩进使输出更美观(可选)  
      
        // 释放 JSON 对象(注意:json_dumps 之后不需要再释放 root,因为 json_dumps 会复制数据)  
        json_decref(root);  
      
        return json_str;  
    }  
      
    int main(void) {  
        HAL_Init();  
        SystemClock_Config();  
        MX_GPIO_Init();  
        MX_DMA_Init();  
        MX_USART1_UART_Init();  
      
        printf("Hello World!\r\n");  
      
        while (1) {  
            Student_TypeDef student = {"Mei Zhou", "Huan", "123", 19};  
            char *json_str = create_json_string(&student);  
            if (json_str) {  
                printf("Generated JSON:\r\n%s\r\n", json_str);  
                free(json_str); // 释放动态分配的 JSON 字符串内存  
            } else {  
                printf("Failed to generate JSON\r\n");  
            }  
            HAL_Delay(5000);  
        }  
    }

    代码2运行效果:

    补充

     在最后组装json数据时,这里可以填一些参数

    char *jsonString = json_dumps(root, JSON_INDENT(2));  

    分别为:

    #define JSON_MAX_INDENT         0x1F
    #define JSON_INDENT(n)          ((n) & JSON_MAX_INDENT)
    #define JSON_COMPACT            0x20
    #define JSON_ENSURE_ASCII       0x40
    #define JSON_SORT_KEYS          0x80
    #define JSON_PRESERVE_ORDER     0x100
    #define JSON_ENCODE_ANY         0x200
    #define JSON_ESCAPE_SLASH       0x400
    #define JSON_REAL_PRECISION(n)  (((n) & 0x1F) << 11)
    宏定义 说明
    JSON_MAX_INDENT 0x1F 最大缩进级别(用于格式化输出时的缩进限制)
    JSON_INDENT(n) ((n) & JSON_MAX_INDENT) 设置JSON输出的缩进级别为n,实际缩进级别会被限制在JSON_MAX_INDENT范围内
    JSON_COMPACT 0x20 生成紧凑的JSON字符串(无缩进和多余的空格)
    JSON_ENSURE_ASCII 0x40 确保输出的JSON字符串仅包含ASCII字符,对非ASCII字符进行转义
    JSON_SORT_KEYS 0x80 对JSON对象的键进行排序输出
    JSON_PRESERVE_ORDER 0x100 保留JSON对象插入键值的顺序(不排序)
    JSON_ENCODE_ANY 0x200 允许编码任何类型的JSON值(包括非标准的或自定义的类型)
    JSON_ESCAPE_SLASH 0x400 对正斜杠(/)进行转义处理
    JSON_REAL_PRECISION(n) (((n) & 0x1F) << 11) 设置浮点数的精度为nn的有效范围是0到31)

    用法:

    char *jsonString = json_dumps(root, JSON_INDENT(2) | JSON_ENSURE_ASCII | JSON_REAL_PRECISION(5));

    这个示例中,json_dumps函数被配置为:

  • 使用2个空格缩进
  • 确保输出为ASCII字符
  • 设置浮点数的精度为5
  • 结尾(必读)

     这里注意一点:
    如果Json解析或者组装代码是正确的,然后解析或者组装的内容与预期不符,很可能是程序中设置的堆大小不足,
    解决方法:在启动文件中:startup_stm32f103xb.s中找到

    Heap_Size      EQU     0x200

    修改为(也可以在STM32CubeMX软件代码生成时修改)

    Heap_Size      EQU     0x800

    在json解析和组装代码中存在大量的if判断结果是否正确,为了代码的健壮性一般不建议删除这些判断

    后续更新更多的API使用
     

    作者:送外卖的CV工程师

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32在Keil5中利用Jansson库处理和组装JSON数据【详细版】

    发表回复