C语言如何解析嵌套的JSON数据
在C语言开发中,处理JSON数据是常见需求,尤其是从API响应、配置文件等场景获取嵌套结构的JSON时,由于C语言本身没有内置的JSON解析库,我们需要借助第三方库来实现,本文将以轻量级且广泛使用的cJSON库为例,详细介绍如何在C语言中解析嵌套的JSON数据,包括环境搭建、数据解析流程、错误处理及完整代码示例。
为什么选择cJSON库?
cJSON是一个用C语言编写的轻量级JSON解析器,具有以下特点:
- 轻量级:核心代码仅约2000行,编译后体积小,适合嵌入式和资源受限环境。
- 易用性:提供简洁的API,支持JSON的创建、解析、修改和序列化。
- 功能完整:支持嵌套对象、数组、字符串、数字、布尔值、null等JSON数据类型。
这些特点使其成为C语言处理JSON的首选工具。
环境搭建:安装cJSON库
获取cJSON源码
cJSON的官方仓库为https://github.com/DaveGamble/cJSON,你可以通过以下方式获取:
git clone https://github.com/DaveGamble/cJSON.git
或直接从仓库下载cJSON.h和cJSON.c文件。
集成到项目
将cJSON.h(头文件)和cJSON.c(源文件)添加到你的C语言项目中,在Visual Studio、CLion或gcc编译时,包含源文件即可:
gcc your_program.c cJSON.c -o your_program -lm
(注意:cJSON中的浮点数运算可能需要链接数学库,因此添加-lm选项。)
JSON数据结构回顾
在解析前,需明确JSON的基本结构,嵌套JSON通常包含以下两种核心结构:
- 对象(Object):无序的键值对集合,用表示,如
{"name": "Alice", "age": 30}。 - 数组(Array):有序的值列表,用
[]表示,如[1, 2, {"key": "value"}]。
嵌套结构即对象或数组内部包含其他对象或数组,
{
"name": "Bob",
"age": 25,
"courses": [
{"title": "Math", "credits": 4},
{"title": "Physics", "credits": 3}
],
"address": null
}
解析嵌套JSON的步骤
使用cJSON解析嵌套JSON的核心流程可概括为:解析字符串 → 遍历JSON树 → 递归/循环访问嵌套节点,以下是详细步骤:
步骤1:将JSON字符串解析为cJSON对象
使用cJSON_Parse()函数将JSON格式的字符串解析为cJSON对象指针,该函数返回一个cJSON*类型指针,指向解析后的JSON树根节点;若解析失败(如JSON格式错误),则返回NULL。
#include <stdio.h>
#include <stdlib.h>
#include "cJSON.h"
int main() {
const char *json_string = "{"
"\"name\": \"Bob\","
"\"age\": 25,"
"\"courses\": ["
"{\"title\": \"Math\", \"credits\": 4},"
"{\"title\": \"Physics\", \"credits\": 3}"
"],"
"\"address\": null"
"}";
// 解析JSON字符串
cJSON *root = cJSON_Parse(json_string);
if (root == NULL) {
const char *error_ptr = cJSON_GetErrorPtr();
if (error_ptr) {
fprintf(stderr, "Error before: %s\n", error_ptr);
}
return 1;
}
// 后续解析操作...
cJSON_Delete(root); // 释放内存
return 0;
}
注意:cJSON_Parse()会动态分配内存,解析完成后必须调用cJSON_Delete()释放整个JSON树的内存,否则会导致内存泄漏。
步骤2:根据键获取对象中的值
若JSON根节点是对象,可通过cJSON_GetObjectItem()函数根据键(key)获取对应的值节点,该函数返回cJSON*指针,指向目标值的节点。
// 获取"name"字段(字符串类型)
cJSON *name_item = cJSON_GetObjectItem(root, "name");
if (name_item != NULL && cJSON_IsString(name_item)) {
printf("Name: %s\n", name_item->valuestring);
}
// 获取"age"字段(数字类型)
cJSON *age_item = cJSON_GetObjectItem(root, "age");
if (age_item != NULL && cJSON_IsNumber(age_item)) {
printf("Age: %d\n", age_item->valueint);
}
cJSON数据类型判断:
cJSON_IsString():判断是否为字符串。cJSON_IsNumber():判断是否为数字(包括整数、浮点数)。cJSON_IsBool():判断是否为布尔值。cJSON_IsNull():判断是否为null。cJSON_IsArray():判断是否为数组。cJSON_IsObject():判断是否为对象。
步骤3:解析嵌套数组
当遇到数组类型时,需通过cJSON_GetArraySize()获取数组长度,再使用cJSON_GetArrayItem(index)按索引获取数组元素。
// 获取"courses"数组
cJSON *courses_array = cJSON_GetObjectItem(root, "courses");
if (courses_array != NULL && cJSON_IsArray(courses_array)) {
int course_count = cJSON_GetArraySize(courses_array);
printf("Courses (%d):\n", course_count);
// 遍历数组中的每个元素(每个元素是一个对象)
for (int i = 0; i < course_count; i++) {
cJSON *course_item = cJSON_GetArrayItem(courses_array, i);
if (course_item != NULL && cJSON_IsObject(course_item)) {
// 获取嵌套对象中的字段
cJSON *title_item = cJSON_GetObjectItem(course_item, "title");
cJSON *credits_item = cJSON_GetObjectItem(course_item, "credits");
if (title_item != NULL && cJSON_IsString(title_item) &&
credits_item != NULL && cJSON_IsNumber(credits_item)) {
printf(" - Title: %s, Credits: %d\n",
title_item->valuestring, credits_item->valueint);
}
}
}
}
步骤4:处理嵌套对象
若数组元素或其他字段是嵌套对象,重复步骤2的逻辑即可,若address字段是一个嵌套对象:
"address": {"city": "New York", "zip": "10001"}
解析方式如下:
cJSON *address_item = cJSON_GetObjectItem(root, "address");
if (address_item != NULL && cJSON_IsObject(address_item)) {
cJSON *city_item = cJSON_GetObjectItem(address_item, "city");
cJSON *zip_item = cJSON_GetObjectItem(address_item, "zip");
if (city_item != NULL && cJSON_IsString(city_item) &&
zip_item != NULL && cJSON_IsString(zip_item)) {
printf("Address: %s, %s\n", city_item->valuestring, zip_item->valuestring);
}
}
步骤5:释放内存
解析完成后,必须调用cJSON_Delete(root)释放整个JSON树占用的内存,避免内存泄漏。
cJSON_Delete(root);
完整代码示例
将上述步骤整合,以下是解析嵌套JSON的完整代码:
#include <stdio.h>
#include <stdlib.h>
#include "cJSON.h"
int main() {
const char *json_string = "{"
"\"name\": \"Bob\","
"\"age\": 25,"
"\"courses\": ["
"{\"title\": \"Math\", \"credits\": 4},"
"{\"title\": \"Physics\", \"credits\": 3}"
"],"
"\"address\": {\"city\": \"New York\", \"zip\": \"10001\"}"
"}";
// 1. 解析JSON字符串
cJSON *root = cJSON_Parse(json_string);
if (root == NULL) {
const char *error_ptr = cJSON_GetErrorPtr();
if (error_ptr) {
fprintf(stderr, "JSON解析错误: %s\n", error_ptr);
}
return 1;
}
// 2. 解析顶层字段
cJSON *name_item = cJSON_GetObjectItem(root, "name");
cJSON *age_item = cJSON_GetObjectItem(root, "age");
if (name_item != NULL && cJSON_IsString(name_item) &&
age_item != NULL && cJSON_IsNumber(age_item)) {
printf("姓名: %s


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