C语言实战:使用cJSON库精通JSON文件的读写与修改

在C语言开发中,处理结构化数据往往是一个棘手的问题。你是否曾为如何将复杂的配置保存到文件,或者如何解析服务器返回的JSON数据而感到困扰?作为一种轻量级的数据交换格式,JSON(JavaScript Object Notation)已经成为现代应用开发中事实上的标准。然而,C语言本身并不原生支持JSON,这就需要我们借助强大的第三方库来弥补这一短板。

在这篇文章中,我们将深入探讨如何使用 cJSON 这个超轻量级、开源的C语言库来处理JSON数据。我们将不仅仅局限于简单的读写,还会通过完整的代码示例,带你一步步实现将数据写入文件、从文件读取并解析数据,以及如何修改现有的JSON文件。无论你是在编写嵌入式系统、后端服务还是桌面应用,掌握这些技能都将极大地提升你的开发效率。

为什么选择 cJSON?

在C语言生态系统中,处理JSON的库有很多,但cJSON因其独特的优势而广受欢迎。首先,它完全由ANSI C(C89)编写,这意味着它具有极高的可移植性,几乎可以在任何平台上编译和运行。其次,它遵循MIT许可证,你可以自由地在商业项目中使用它而无需担心版权问题。最关键的是,cJSON没有任何外部依赖,你只需要将两个文件(INLINECODEae27f4e6 和 INLINECODE23c02f5f)添加到你的项目中即可开始工作,这非常符合C语言“极简主义”的哲学。

准备工作

在开始编码之前,你需要确保开发环境中已经包含了cJSON库。你可以从其GitHub仓库下载源码,或者通过包管理器(如Linux下的apt或yum)安装。确保你的编译器能够找到 INLINECODE51bcea92 头文件,并且在链接时能够链接到 INLINECODE63b181d1 库文件(例如使用 -lcjson 标志)。

第一步:构建并写入 JSON 数据

让我们从最基础的操作开始:创建一个JSON对象并将其保存到文件中。想象一下,你正在编写一个用户管理模块,需要将用户的个人信息保存到磁盘。

在这个过程中,我们需要做三件事:

  • 创建一个 cJSON 对象并在内存中构建数据结构。
  • 将这个内存中的结构转换为格式化的 JSON 字符串。
  • 将字符串通过标准 C 文件操作写入磁盘。

下面是一个完整的代码示例,演示了如何创建包含用户名、年龄和邮箱的 JSON 数据,并将其写入 user_data.json 文件。

#include 
#include 
#include 
#include "cJSON.h"

// 辅助函数:创建示例JSON数据并保存到文件
void write_json_to_file(const char *filename) {
    // 1. 创建根对象
    cJSON *root = cJSON_CreateObject();
    if (root == NULL) {
        fprintf(stderr, "错误:无法创建JSON根对象
");
        return;
    }

    // 2. 向对象中添加键值对
    // 注意:cJSON_AddStringToObject 是一个宏,简化了添加字符串的操作
    cJSON_AddStringToObject(root, "name", "张三");
    cJSON_AddNumberToObject(root, "age", 28);
    cJSON_AddStringToObject(root, "city", "北京");
    cJSON_AddStringToObject(root, "email", "[email protected]");
    
    // 我们甚至可以添加一个布尔值
    cJSON_AddBoolToObject(root, "is_verified", 1);

    // 3. 将 cJSON 对象打印为字符串
    // cJSON_Print 会分配一块内存并返回格式化好的字符串
    char *json_string = cJSON_Print(root);
    if (json_string == NULL) {
        fprintf(stderr, "错误:无法打印JSON数据
");
        cJSON_Delete(root);
        return;
    }

    // 4. 打开文件并写入
    FILE *fp = fopen(filename, "w");
    if (fp == NULL) {
        fprintf(stderr, "错误:无法打开文件 %s
", filename);
        // 记得释放已分配的内存
        free(json_string);
        cJSON_Delete(root);
        return;
    }

    fputs(json_string, fp);
    fclose(fp);

    // 5. 清理资源
    // 释放 cJSON_Print 返回的字符串必须使用标准 free,而不是 cJSON_free(取决于cJSON版本和配置,通常两者兼容)
    free(json_string); 
    // 释放 cJSON 根对象及其所有子节点
    cJSON_Delete(root);
    
    printf("数据已成功写入 %s
", filename);
}

int main() {
    write_json_to_file("user_data.json");
    return 0;
}

运行这段代码后,你将得到一个格式整齐的 user_data.json 文件。让我们看看里面的内容:

{
    "name":  "张三",
    "age":   28,
    "city":  "北京",
    "email": "[email protected]",
    "is_verified": true
}
``

**代码解析:**
在这个例子中,我们使用了 `cJSON_CreateObject` 来创建一个空的JSON对象。通过 `cJSON_AddStringToObject` 和 `cJSON_AddNumberToObject` 等辅助函数,我们可以轻松地向对象中填充数据。cJSON 库的一个优雅之处在于它采用了链表和树的组合结构来管理数据,因此当我们调用 `cJSON_Delete(root)` 时,它会递归地释放所有子节点的内存,防止内存泄漏。这对于习惯了手动管理内存的 C 程序员来说非常重要。

### 第二步:从文件读取并解析 JSON 数据

数据已经保存了,现在我们需要把它读回来。在 C 语言中,读取文件通常涉及到缓冲区的管理。使用 cJSON 时,我们需要先将整个文件内容读入一个字符串缓冲区,然后使用 `cJSON_Parse` 进行解析。

这里有一个关键的实践点:如何处理文件大小不确定的情况?在下面的例子中,我们将展示一种更健壮的方法来读取文件,并加入错误处理机制。

c

#include

#include

#include

#include "cJSON.h"

// 读取整个文件到字符串的辅助函数

char readfilecontents(const char filename) {

FILE *fp = fopen(filename, "r");

if (fp == NULL) return NULL;

// 移动到文件末尾以获取大小

fseek(fp, 0, SEEK_END);

long length = ftell(fp);

fseek(fp, 0, SEEK_SET);

// 分配内存 (+1 用于 null 终止符)

char buffer = (char )malloc(length + 1);

if (buffer) {

fread(buffer, 1, length, fp);

buffer[length] = ‘\0‘;

}

fclose(fp);

return buffer;

}

void parsejsonfile(const char *filename) {

// 1. 读取文件内容

char *jsoncontent = readfile_contents(filename);

if (!json_content) {

fprintf(stderr, "无法读取文件: %s

", filename);

return;

}

// 2. 解析 JSON 字符串

cJSON *json = cJSONParse(jsoncontent);

if (json == NULL) {

// 解析失败时,获取错误指针

const char *errorptr = cJSONGetErrorPtr();

if (error_ptr != NULL) {

fprintf(stderr, "JSON 解析错误: %s

", error_ptr);

}

free(json_content);

return;

}

// 3. 获取数据项

// cJSON_GetObjectItemCaseSensitive 是区分大小写的查找,性能通常更好

cJSON *nameitem = cJSONGetObjectItemCaseSensitive(json, "name");

cJSON *ageitem = cJSONGetObjectItemCaseSensitive(json, "age");

cJSON *emailitem = cJSONGetObjectItemCaseSensitive(json, "email");

// 4. 检查类型并打印

if (cJSONIsString(nameitem) && (name_item->valuestring != NULL)) {

printf("读取到用户名: %s

", name_item->valuestring);

}

// 对于数字,我们通常先检查是否为Number类型

if (cJSONIsNumber(ageitem)) {

printf("读取到年龄: %d

", age_item->valueint);

}

if (cJSONIsString(emailitem)) {

printf("读取到邮箱: %s

", email_item->valuestring);

}

// 5. 清理

cJSON_Delete(json);

free(json_content);

}

int main() {

// 假设我们已经有上面步骤生成的 user_data.json

parsejsonfile("user_data.json");

return 0;

}


**输出示例:**

读取到用户名: 张三

读取到年龄: 28

读取到邮箱: [email protected]


**实用见解:**
在解析 JSON 时,类型检查至关重要。C 语言是弱类型的,而 JSON 数据可能是动态的。如果服务器发送的 JSON 中 `age` 字段变成了字符串(例如 "28"),而你直接调用 `age_item->valueint`,程序可能会崩溃或输出错误数据。因此,始终使用 `cJSON_IsNumber`、`cJSON_IsString` 等宏来进行验证,是编写健壮代码的关键。

### 第三步:修改现有的 JSON 数据

实际开发中,我们经常需要修改配置文件。比如,我们需要更新用户的年龄,或者添加一个新的字段。cJSON 并没有直接提供“修改文件”的函数,因为 JSON 是树状结构。要修改数据,我们需要遵循“读取 -> 修改 -> 覆盖写入”的模式。

下面的例子展示了如何修改 JSON 文件中的数据,并添加一个新的字段(比如“职业”),同时演示如何处理数组。

c

#include

#include "cJSON.h"

void modifyjsondata(const char *filename) {

// 第一步:读取并解析(复用上面的逻辑)

FILE *fp = fopen(filename, "r");

if (!fp) return;

fseek(fp, 0, SEEK_END);

long len = ftell(fp);

fseek(fp, 0, SEEK_SET);

char data = (char )malloc(len + 1);

fread(data, 1, len, fp);

fclose(fp);

cJSON *root = cJSON_Parse(data);

free(data);

if (!root) {

printf("解析失败

");

return;

}

// 第二步:修改现有数据

// 替换 age 字段:如果存在则删除旧的,添加新的;或者直接替换内部值

cJSON *ageitem = cJSONGetObjectItemCaseSensitive(root, "age");

if (age_item) {

cJSONSetNumberValue(ageitem, 29); // 年龄+1

printf("年龄已更新

");

}

// 第三步:添加新数据

cJSON_AddStringToObject(root, "occupation", "软件工程师");

// 第四步:处理数组 – 添加一个技能列表

// 创建一个数组对象

cJSON *skills = cJSON_CreateArray();

cJSONAddItemToArray(skills, cJSONCreateString("C语言"));

cJSONAddItemToArray(skills, cJSONCreateString("Python"));

cJSONAddItemToArray(skills, cJSONCreateString("系统架构"));

// 将数组挂载到根对象上

cJSON_AddItemToObject(root, "skills", skills);

// 第五步:将修改后的对象打印成字符串并保存

char *updatedjson = cJSONPrint(root);

// 以 "w" 模式打开文件会清空原内容,实现覆盖写入

fp = fopen(filename, "w");

if (fp) {

fputs(updated_json, fp);

fclose(fp);

printf("文件修改成功!

");

}

// 清理

free(updated_json);

cJSON_Delete(root);

}

int main() {

modifyjsondata("user_data.json");

return 0;

}


**修改后的 JSON 文件内容:**

json

{

"name": "张三",

"age": 29,

"city": "北京",

"email": "[email protected]",

"is_verified": true,

"occupation": "软件工程师",

"skills": ["C语言", "Python", "系统架构"]

}

“INLINECODE45398f96cJSONCreateObjectINLINECODEc8b0aef0CreateINLINECODE1316fbd1DeleteINLINECODEaa981ba0cJSONPrintINLINECODE8cdaca86cJSONPrintUnformattedINLINECODE63f6a81dcJSONParseINLINECODE42bd82a9cJSONGetErrorPtrINLINECODEf6b35020cJSONParseWithOptsINLINECODE9746efdbundefined reference to cJSONCreateObject),请记住检查编译命令是否包含了 -lcjson`。

希望这篇指南能帮助你更好地在 C 语言项目中驾驭 JSON 数据。祝你编码愉快!

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。如需转载,请注明文章出处豆丁博客和来源网址。https://shluqu.cn/21948.html
点赞
0.00 平均评分 (0% 分数) - 0