C语言如何读取JSON文件:从入门到实践
在C语言开发中,处理JSON(JavaScript Object Notation)数据是一项常见需求,JSON因其轻量级、易读的结构特性,成为前后端数据交换的主流格式,C语言本身没有内置的JSON解析库,因此我们需要借助第三方库来完成JSON文件的读取与解析,本文将以cJSON这一轻量级、开源的C语言JSON解析库为例,详细讲解如何在C语言中读取并解析JSON文件,从环境搭建到具体实践,助你快速这一技能。
为什么选择cJSON?
在众多C语言JSON库中(如Jansson、Parson等),cJSON因以下优势被广泛使用:
- 轻量级:核心代码仅包含单个
cJSON.c和cJSON.h文件,无需额外依赖,易于集成。 - 高效简洁:采用链表+哈希表的数据结构,解析速度快,API设计直观。
- 功能完整:支持JSON的创建、解析、修改、序列化等全流程操作,满足大多数场景需求。
环境准备:安装cJSON库
获取cJSON源码
cJSON的官方GitHub仓库为:https://github.com/DaveGamble/cJSON
你可以直接下载源码压缩包,或通过git克隆:
git clone https://github.com/DaveGamble/cJSON.git
编译静态库(可选)
如果项目需要将cJSON作为静态库引用,可以进入cJSON目录,执行以下命令生成libcjson.a(Linux/macOS):
mkdir build && cd build cmake .. -DENABLE_CJSON_TEST=OFF -DENABLE_CJSON_UTILS=OFF -DENABLE_CJSON_API=ON make
编译完成后,libcjson.a会位于build目录下,cJSON.h在cJSON根目录。
直接集成源码(推荐)
对于简单项目,最直接的方式是直接将cJSON.c和cJSON.h复制到你的工程目录中,然后在编译时包含cJSON.c即可。
gcc your_program.c cJSON.c -o your_program -lm
(注意:cJSON使用了math.h中的函数,需链接-lm库)
读取JSON文件的完整流程
假设我们有一个名为config.json的JSON文件,内容如下:
{
"name": "C Language JSON Demo",
"version": "1.0",
"isDebug": true,
"count": 100,
"dependencies": [
"cJSON",
"libcurl",
"openssl"
],
"info": {
"author": "Developer",
"email": "dev@example.com"
}
}
我们的目标是读取该文件,并解析其中的各个字段,以下是具体步骤:
步骤1:打开JSON文件并读取内容
C语言中,通过文件操作函数(fopen、fread等)读取JSON文件的全部内容到内存中。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 读取文件内容到字符串缓冲区
char* read_file(const char* filepath) {
FILE* file = fopen(filepath, "rb"); // 以二进制模式打开,避免换行符问题
if (!file) {
perror("Failed to open file");
return NULL;
}
// 获取文件大小
fseek(file, 0, SEEK_END);
long file_size = ftell(file);
fseek(file, 0, SEEK_SET);
// 分配内存(+1用于字符串结束符'\0')
char* buffer = (char*)malloc(file_size + 1);
if (!buffer) {
perror("Failed to allocate memory");
fclose(file);
return NULL;
}
// 读取文件内容
size_t bytes_read = fread(buffer, 1, file_size, file);
if (bytes_read != file_size) {
perror("Failed to read file");
free(buffer);
fclose(file);
return NULL;
}
buffer[bytes_read] = '\0'; // 确保字符串以'\0'
fclose(file);
return buffer;
}
步骤2:使用cJSON解析JSON字符串
后,通过cJSON_Parse()函数将JSON字符串解析为cJSON对象(链表结构)。
#include "cJSON.h"
int main() {
const char* filepath = "config.json";
char* json_string = read_file(filepath);
if (!json_string) {
fprintf(stderr, "Error reading JSON file\n");
return -1;
}
// 解析JSON字符串
cJSON* root = cJSON_Parse(json_string);
if (!root) {
const char* error_ptr = cJSON_GetErrorPtr();
if (error_ptr) {
fprintf(stderr, "JSON parse error before: %s\n", error_ptr);
} else {
fprintf(stderr, "JSON parse error\n");
}
free(json_string);
return -1;
}
// 解析完成后,释放JSON字符串内存
free(json_string);
步骤3:解析JSON数据(键值对、数组、嵌套对象)
cJSON通过链表节点表示JSON数据,每个节点可能是对象(cJSON_Object)、数组(cJSON_Array)、字符串(cJSON_String)、数字(cJSON_Number)等类型,常用API如下:
cJSON_GetObjectItemCaseSensitive(root, key):从对象中获取指定键的节点(区分大小写)。cJSON_IsString(node)、cJSON_IsNumber(node)、cJSON_IsBool(node)等:判断节点类型。node->valuestring、node->valueint、node->valuedouble等:获取节点值。
示例:解析config.json中的数据
// 1. 解析字符串字段
cJSON* name_node = cJSON_GetObjectItemCaseSensitive(root, "name");
if (cJSON_IsString(name_node)) {
printf("Name: %s\n", name_node->valuestring);
}
// 2. 解析数字字段(注意:JSON中数字不区分int/float,需根据需求选择)
cJSON* version_node = cJSON_GetObjectItemCaseSensitive(root, "version");
if (cJSON_IsString(version_node)) {
printf("Version: %s\n", version_node->valuestring);
}
cJSON* count_node = cJSON_GetObjectItemCaseSensitive(root, "count");
if (cJSON_IsNumber(count_node)) {
printf("Count: %d\n", count_node->valueint); // 或count_node->valuedouble
}
// 3. 解析布尔值字段
cJSON* is_debug_node = cJSON_GetObjectItemCaseSensitive(root, "isDebug");
if (cJSON_IsBool(is_debug_node)) {
printf("IsDebug: %s\n", cJSON_IsTrue(is_debug_node) ? "true" : "false");
}
// 4. 解析数组字段
cJSON* deps_node = cJSON_GetObjectItemCaseSensitive(root, "dependencies");
if (cJSON_IsArray(deps_node)) {
int array_size = cJSON_GetArraySize(deps_node);
printf("Dependencies (%d items):\n", array_size);
for (int i = 0; i < array_size; i++) {
cJSON* item = cJSON_GetArrayItem(deps_node, i);
if (cJSON_IsString(item)) {
printf(" - %s\n", item->valuestring);
}
}
}
// 5. 解析嵌套对象
cJSON* info_node = cJSON_GetObjectItemCaseSensitive(root, "info");
if (cJSON_IsObject(info_node)) {
cJSON* author_node = cJSON_GetObjectItemCaseSensitive(info_node, "author");
cJSON* email_node = cJSON_GetObjectItemCaseSensitive(info_node, "email");
if (cJSON_IsString(author_node) && cJSON_IsString(email_node)) {
printf("Info - Author: %s, Email: %s\n", author_node->valuestring, email_node->valuestring);
}
}
步骤4:释放cJSON对象内存
cJSON的内存是通过cJSON_Parse()动态分配的,使用完毕后必须调用cJSON_Delete()释放,否则会导致内存泄漏。
// 释放cJSON对象
cJSON_Delete(root);
return 0;
}
完整代码示例
将上述步骤整合,完整的read_json.c代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cJSON.h"
// 读取文件内容到字符串缓冲区
char* read_file(const char* filepath) {
FILE* file = fopen(filepath, "rb");
if (!file)


还没有评论,来说两句吧...