STM32F103平台Jansson库集成与JSON解析实践
在嵌入式领域处理JSON数据时,内存受限是单片机开发的核心挑战。与依赖动态内存分配的cJSON不同,Jansson提供了更轻量的解决方案,尤其适合Keil MDK环境下的ARM Cortex-M3项目。本文基于STM32F103芯片,演示Jansson的集成流程与典型解析场景。
开发环境配置
从Keil官方获取Jansson Pack包(Keil.Jansson.1.0.0.pack)并完成安装。在工程选项中启用该组件后,编译系统会自动链接jansson_CM3LE.lib静态库,同时需将jansson_config.c加入项目。源码文件顶部引入头文件:
#include <jansson.h>
调试环节采用自定义串口输出接口替代标准printf,避免依赖重定向:
void debug_printf(const char *format, ...)
{
uint8_t tx_buffer[256];
va_list args;
va_start(args, format);
vsnprintf((char *)tx_buffer, sizeof(tx_buffer), format, args);
va_end(args);
for (uint8_t *ptr = tx_buffer; *ptr != '\0'; ptr++) {
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
USART_SendData(USART1, *ptr);
}
}
场景一:基础数据类型提取
以下JSON包含字符串、数值与布尔值的混合结构:
{
"device_id": "STM32_001",
"firmware_ver": 3.14,
"sample_rate": 1000,
"is_active": true
}
解析时通过类型专用API获取值,注意布尔量需先判断类型再取值:
uint8_t parse_basic_types(void)
{
const char *payload = "{\"device_id\":\"STM32_001\",\"firmware_ver\":3.14,\"sample_rate\":1000,\"is_active\":true}";
json_error_t err_info;
json_t *doc = json_loads(payload, 0, &err_info);
if (!json_is_object(doc)) {
debug_printf("Parse failed at line %d: %s\r\n", err_info.line, err_info.text);
return 1;
}
const char *dev_name = json_string_value(json_object_get(doc, "device_id"));
double fw_version = json_real_value(json_object_get(doc, "firmware_ver"));
int32_t rate = json_integer_value(json_object_get(doc, "sample_rate"));
json_t *active_flag = json_object_get(doc, "is_active");
if (json_is_true(active_flag)) {
debug_printf("Device %s online, FW:%.2f, %dHz\r\n", dev_name, fw_version, rate);
}
json_decref(doc);
return 0;
}
场景二:嵌套对象遍历
当键值指向另一个对象时,需分层解析。以下结构包含时间戳与日期信息的二级嵌套:
{
"code": 200,
"data": {
"unix_ts": "1719820800",
"iso_date": "2024-07-01T00:00:00Z",
"day_of_year": 183
}
}
外层获取data节点后,将其作为新的根节点继续提取内层字段:
uint8_t parse_nested_object(void)
{
const char *payload = "{\"code\":200,\"data\":{\"unix_ts\":\"1719820800\",\"iso_date\":\"2024-07-01T00:00:00Z\",\"day_of_year\":183}}";
json_t *root = json_loads(payload, 0, NULL);
json_t *data_node = json_object_get(root, "data");
if (json_is_object(data_node)) {
const char *timestamp = json_string_value(json_object_get(data_node, "unix_ts"));
const char *iso = json_string_value(json_object_get(data_node, "iso_date"));
int32_t doy = json_integer_value(json_object_get(data_node, "day_of_year"));
debug_printf("TS:%s ISO:%s DOY:%d\r\n", timestamp, iso, doy);
}
json_decref(root);
return 0;
}
场景三:数组结构处理
传感器批量上报场景常见数组结构,每个元素为独立对象:
{
"sensor_list": [
{"ch": 0, "val": 23.5, "unit": "C"},
{"ch": 1, "val": 56.2, "unit": "%RH"}
],
"record_count": 2
}
利用json_array_size获取长度,通过索引遍历元素:
uint8_t parse_sensor_array(void)
{
const char *payload = "{\"sensor_list\":[{\"ch\":0,\"val\":23.5,\"unit\":\"C\"},{\"ch\":1,\"val\":56.2,\"unit\":\"%RH\"}],\"record_count\":2}";
json_t *root = json_loads(payload, 0, NULL);
json_t *sensors = json_object_get(root, "sensor_list");
size_t item_cnt = json_array_size(sensors);
for (size_t i = 0; i < item_cnt; i++) {
json_t *item = json_array_get(sensors, i);
int channel = json_integer_value(json_object_get(item, "ch"));
double reading = json_real_value(json_object_get(item, "val"));
const char *unit = json_string_value(json_object_get(item, "unit"));
debug_printf("[%zu] CH%d: %.1f%s\r\n", i, channel, reading, unit);
}
json_decref(root);
return 0;
}
内存优化注意事项
若遇到解析异常或HardFault,优先检查启动文件中的堆配置。在startup_stm32f10x_md.s中调整Heap_Size定义,典型值设为0x00000200以上。另外Jansson对UTF-8编码的兼容性有限,JSON字符串中避免直接包含中文字符,建议采用Base64编码或Unicode转义序列传递非ASCII内容。