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


下载Keil.Jansson.1.0.0.pack

https://keilpack.azureedge.net/pack/Keil.Jansson.1.0.0.packicon-default.png?t=O83Ahttps://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使用和接收icon-default.png?t=O83Ahttps://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_INDENT0x1F最大缩进级别(用于格式化输出时的缩进限制)
JSON_INDENT(n)((n) & JSON_MAX_INDENT)设置JSON输出的缩进级别为n,实际缩进级别会被限制在JSON_MAX_INDENT范围内
JSON_COMPACT0x20生成紧凑的JSON字符串(无缩进和多余的空格)
JSON_ENSURE_ASCII0x40确保输出的JSON字符串仅包含ASCII字符,对非ASCII字符进行转义
JSON_SORT_KEYS0x80对JSON对象的键进行排序输出
JSON_PRESERVE_ORDER0x100保留JSON对象插入键值的顺序(不排序)
JSON_ENCODE_ANY0x200允许编码任何类型的JSON值(包括非标准的或自定义的类型)
JSON_ESCAPE_SLASH0x400对正斜杠(/)进行转义处理
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使用
 

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐