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

发布于:2025-08-01 ⋅ 阅读:(15) ⋅ 点赞:(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 数据,为跨平台数据交互提供可靠支持。