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

发布于:2025-08-01 ⋅ 阅读:(16) ⋅ 点赞:(0)

       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"),  // 目标 IP
    8888                             // 目标端口
);

// 释放资源
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 数据,为跨平台数据交互提供可靠支持。


网站公告

今日签到

点亮在社区的每一天
去签到