当前位置:首页 > 技术 > 正文内容

STM32F103平台Jansson库集成与JSON解析实践

访客 技术 2026年6月7日 1

在嵌入式领域处理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内容。

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。