当前位置: 首页 > news >正文

Qt之CJSON:从基础到进阶的 JSON 数据处理指南

       JSON(JavaScript Object Notation)作为一种轻量级的数据交换格式,在跨平台通信、配置存储等场景中被广泛使用。而 CJSON 作为一款轻量级的 C 语言 JSON 库,凭借其简洁的 API 和极小的体积,成为 Qt 项目中处理 JSON 数据的理想选择。本文将从 CJSON 基础入手,逐步讲解其在 Qt 中的使用方法,包括数据组装、发送、解析及进阶的 JSON 流处理,并总结关键注意事项。

一、什么是 CJSON?

CJSON 是由 Dave Gamble 开发的一款开源 JSON 解析库,其核心优势在于:

  • 轻量简洁:仅包含 cJSON.c 和 cJSON.h 两个文件,无需依赖其他库,可直接嵌入项目。
  • API 直观:提供了创建、解析、修改 JSON 数据的全套接口,上手成本低。
  • 兼容性强:支持标准 JSON 语法(对象、数组、字符串、数字等类型),可在 C/C++ 项目中无缝使用。

在 Qt 项目中集成 CJSON 非常简单:只需将 cJSON.c 和 cJSON.h 复制到项目目录,并在 .pro 文件中添加源文件即可:

SOURCES += cJSON.c  # 确保路径正确

二、Qt 中常用的 CJSON 函数及操作

       CJSON 的核心操作围绕 “创建 JSON 数据” 和 “解析 JSON 数据” 展开,以下是 Qt 中最常用的函数及用法:

1. JSON 数据的创建与修改

函数功能示例
cJSON_CreateObject()创建 JSON 对象({}cJSON* obj = cJSON_CreateObject();
cJSON_CreateArray()创建 JSON 数组([]cJSON* arr = cJSON_CreateArray();
cJSON_AddStringToObject()向对象添加字符串字段cJSON_AddStringToObject(obj, "name", "Qt");
cJSON_AddNumberToObject()向对象添加数字字段cJSON_AddNumberToObject(obj, "version", 6.5);
cJSON_AddBoolToObject()向对象添加布尔字段cJSON_AddBoolToObject(obj, "enable", true);
cJSON_AddItemToArray()向数组添加元素cJSON_AddItemToArray(arr, cJSON_CreateString("item1"));
cJSON_PrintUnformatted()将 JSON 转为紧凑字符串(无格式)char* str = cJSON_PrintUnformatted(obj);
cJSON_Print()将 JSON 转为带格式字符串(有缩进)char* str = cJSON_Print(obj);(适合调试)

2. JSON 数据的解析与查询

函数功能示例
cJSON_Parse()解析 JSON 字符串为 cJSON 结构体cJSON* root = cJSON_Parse(jsonStr);
cJSON_GetObjectItem()从对象中获取字段cJSON* item = cJSON_GetObjectItem(root, "name");
cJSON_GetArraySize()获取数组长度int size = cJSON_GetArraySize(arr);
cJSON_GetArrayItem()获取数组中指定索引的元素cJSON* elem = cJSON_GetArrayItem(arr, 0);
cJSON_IsString() / cJSON_IsNumber()判断字段类型if (cJSON_IsString(item)) { ... }
cJSON_GetErrorPtr()获取解析错误信息const char* err = cJSON_GetErrorPtr();
cJSON_Delete()释放 cJSON 结构体内存cJSON_Delete(root);

3. 类型判断与值提取

解析 JSON 时,需先判断字段类型,再提取对应的值:

  • 字符串:item->valuestring(需先通过 cJSON_IsString(item) 验证)
  • 数字:item->valuedouble(浮点数)或 item->valueint(整数)
  • 布尔:cJSON_IsTrue(item) 或 cJSON_IsFalse(item) 判断

三、基础实战:简单 CJSON 数据的组装、发送与解析

以 “通过 UDP 发送一条包含设备信息的 JSON 数据,并在接收端解析” 为例,演示完整流程。

1. 组装并发送 JSON 数据

// 组装 JSON 数据
cJSON* deviceInfo = cJSON_CreateObject();
cJSON_AddStringToObject(deviceInfo, "id", "dev_001");
cJSON_AddNumberToObject(deviceInfo, "temp", 25.3);
cJSON_AddBoolToObject(deviceInfo, "online", true);// 转为字符串(紧凑格式,适合传输)
char* jsonStr = cJSON_PrintUnformatted(deviceInfo);
if (!jsonStr) {qDebug() << "JSON 转换失败";return;
}// 通过 UDP 发送(Qt 代码)
QUdpSocket* udpSocket = new QUdpSocket(this);
QByteArray sendData(jsonStr);
udpSocket->writeDatagram(sendData,QHostAddress("192.168.1.100"),  // 目标 IP8888                             // 目标端口
);// 释放资源
free(jsonStr);       // cJSON 生成的字符串需用 free 释放
cJSON_Delete(deviceInfo);  // 释放 cJSON 对象

2. 接收并解析 JSON 数据

// 接收 UDP 数据(Qt 代码)
void onUdpReceived() {while (udpSocket->hasPendingDatagrams()) {QByteArray recvData;recvData.resize(udpSocket->pendingDatagramSize());udpSocket->readDatagram(recvData.data(), recvData.size());// 解析 JSON 数据cJSON* root = cJSON_Parse(recvData.constData());if (!root) {qDebug() << "解析失败:" << cJSON_GetErrorPtr();return;}// 提取字段值cJSON* idItem = cJSON_GetObjectItem(root, "id");if (cJSON_IsString(idItem)) {qDebug() << "设备 ID:" << idItem->valuestring;}cJSON* tempItem = cJSON_GetObjectItem(root, "temp");if (cJSON_IsNumber(tempItem)) {qDebug() << "温度:" << tempItem->valuedouble;}// 释放内存cJSON_Delete(root);}
}

四、进阶实战:JSON 流的组装、发送与解析

        当需要传输多条 JSON 数据(如批量设备信息)时,通常将其封装为 JSON 数组(即 “JSON 流”)。以下是具体实现:

1. 组装并发送 JSON 流(数组形式)

// 组装包含多条数据的 JSON 数组
cJSON* deviceArray = cJSON_CreateArray();// 第一条数据
cJSON* dev1 = cJSON_CreateObject();
cJSON_AddStringToObject(dev1, "id", "dev_001");
cJSON_AddNumberToObject(dev1, "temp", 25.3);
cJSON_AddItemToArray(deviceArray, dev1);// 第二条数据
cJSON* dev2 = cJSON_CreateObject();
cJSON_AddStringToObject(dev2, "id", "dev_002");
cJSON_AddNumberToObject(dev2, "temp", 26.1);
cJSON_AddItemToArray(deviceArray, dev2);// 转为字符串并发送
char* jsonStr = cJSON_PrintUnformatted(deviceArray);
QByteArray sendData(jsonStr);
udpSocket->writeDatagram(sendData, QHostAddress("192.168.1.100"), 8888);// 释放资源
free(jsonStr);
cJSON_Delete(deviceArray);

生成的 JSON 流格式:

json

[{"id":"dev_001","temp":25.3},{"id":"dev_002","temp":26.1}]

2. 解析 JSON 流(数组形式)

void parseJsonStream(const QByteArray& recvData) {cJSON* rootArray = cJSON_Parse(recvData.constData());if (!rootArray || !cJSON_IsArray(rootArray)) {qDebug() << "解析数组失败:" << cJSON_GetErrorPtr();return;}// 遍历数组中的每条数据int arraySize = cJSON_GetArraySize(rootArray);for (int i = 0; i < arraySize; i++) {cJSON* item = cJSON_GetArrayItem(rootArray, i);if (!cJSON_IsObject(item)) continue;// 提取字段const char* id = cJSON_GetObjectItem(item, "id")->valuestring;double temp = cJSON_GetObjectItem(item, "temp")->valuedouble;qDebug() << "第" << i+1 << "条设备:" << id << ",温度:" << temp;}cJSON_Delete(rootArray);
}

五、CJSON 使用注意事项

  1. 内存管理是重中之重

    • cJSON_Parse()cJSON_CreateObject() 等函数会动态分配内存,必须通过 cJSON_Delete() 释放,否则会导致内存泄漏。
    • cJSON_Print() 和 cJSON_PrintUnformatted() 返回的字符串需用 free() 释放(而非 delete),因为其内部使用 malloc 分配内存。
  2. 类型检查不可少
    解析时必须先用 cJSON_IsString()cJSON_IsNumber() 等函数验证字段类型,避免因类型不匹配导致的空指针错误。例如:

    cJSON* item = cJSON_GetObjectItem(root, "filesize");
    if (cJSON_IsNumber(item)) {  // 先检查类型int size = item->valueint;
    }
    

  3. 处理非标准 JSON 需谨慎
    若遇到多条 JSON 拼接的 “非标准流”(如 {"a":1}{"b":2}),需按约定分隔符(如换行)拆分后逐个解析,避免 cJSON_Parse() 解析失败。

  4. 注意数据大小限制
    UDP 传输时,JSON 数据需控制在 MTU 范围内(通常 1500 字节以内),超过时需拆分或改用 TCP。

  5. 避免重复添加字段
    向同一对象添加同名字段时,CJSON 会覆盖旧值,需确保字段名唯一。

六、总结

         CJSON 以其轻量、高效的特点,成为 Qt 项目中处理 JSON 数据的优秀选择。通过本文的介绍,你可以掌握从基础的 JSON 组装、解析,到进阶的 JSON 流处理的全流程,同时注意内存管理、类型检查等关键细节,即可在实际项目中灵活运用。

       无论是设备通信、数据存储还是配置解析,CJSON 都能帮助你简洁、高效地处理 JSON 数据,为跨平台数据交互提供可靠支持。

http://www.lryc.cn/news/606541.html

相关文章:

  • langchain--2--invoke、batch、stream、ainvoke、abatch、astream
  • 远程仓库地址发生变化
  • HTTP性能优化实战
  • Redis实战(4)-- BitMap结构与使用
  • ASIC芯片简介
  • [12月考试] C
  • 【UEFI系列】Event
  • 超简单的跳动爱心
  • 【SpringMVC】拦截器,实现小型登录验证
  • 逻辑回归——银行贷款案例分析
  • Lomsat gelral 树上启发式合并
  • Coze是什么?能做什么?
  • Dify插件安装失败,一直处于安装状态?
  • 【智能体agent】入门之--1.初体验
  • OpenCV学习day2
  • RabbitMQ的特点和消息可靠性保障
  • 【neo4j】跨版本升级数据库
  • 《Java 程序设计》第 14 章 - JavaFX 基础
  • MySQL 8.0 OCP 1Z0-908 题目解析(42)
  • 企业级部署 (基于tomcat与nginx)
  • Linux和shell
  • 【运维基础】Linux 文件系统基本管理
  • Side band ECC、Inline ECC、On-die ECC、Link ECC
  • chrome.storage 和 localStorage
  • Android 基础入门学习目录(持续更新)
  • kettle插件-kettle http client plus插件,轻松解决https接口无法调用文件流下载问题
  • 面试笔记【16:9区域问题】
  • SQL注入SQLi-LABS 靶场less25a-28a详细通关攻略
  • ESP32 外设驱动开发指南 (ESP-IDF框架)——GPIO篇:基础配置、外部中断与PWM(LEDC模块)应用
  • 机械学习--逻辑回归