CJSON 对象拆解全攻略:从基础到实践**
在 C 语言中处理 JSON 数据,cJSON 无疑是一个非常流行且轻量级的库,当我们从 JSON 字符串中解析出一个 cJSON 对象后,常常需要根据业务需求对其进行拆解,以获取特定的值或子结构,本文将详细介绍如何使用 cJSON 库拆分(或者说,访问和提取) cJSON 对象中的各个部分,包括对象、数组以及基本数据类型。
cJSON 对象的基本结构
在拆分之前,我们首先要理解 cJSON 对象的基本结构,一个 cJSON 对象(cJSON *)可以表示以下几种类型:
cJSON_Object:JSON 对象,键值对集合。cJSON_Array:JSON 数组,值的有序列表。cJSON_String:字符串。cJSON_Number:数字(可以是整数或浮点数)。cJSON_True/cJSON_False:布尔值。cJSON_NULL:空值。
拆分 cJSON 对象的核心就是根据其类型,使用 cJSON 提供的相应 API 来访问其内部成员或值。
拆分 JSON 对象(键值对)
当我们处理一个 cJSON_Object 类型的对象时,最常见的需求是根据键(key)来获取对应的值(value)。
根据 key 获取 value 节点
使用 cJSON_GetObjectItemCaseSensitive() 函数(推荐,因为键名大小写敏感)或 cJSON_GetObjectItem() 函数(不区分大小写)可以通过键名获取对应的 cJSON 节点。
#include <stdio.h>
#include <stdlib.h>
#include "cjson/cJSON.h"
void print_json_object(const char *json_string) {
cJSON *root = NULL;
cJSON *name = NULL;
cJSON *age = NULL;
cJSON *is_student = NULL;
// 1. 解析 JSON 字符串
root = cJSON_Parse(json_string);
if (root == NULL) {
const char *error_ptr = cJSON_GetErrorPtr();
if (error_ptr != NULL) {
fprintf(stderr, "Error before: %s\n", error_ptr);
}
return;
}
// 2. 根据 key 拆分获取 value 节点
name = cJSON_GetObjectItemCaseSensitive(root, "name");
age = cJSON_GetObjectItemCaseSensitive(root, "age");
is_student = cJSON_GetObjectItemCaseSensitive(root, "is_student");
// 3. 获取并打印 value 的具体内容
if (cJSON_IsString(name) && (name->valuestring != NULL)) {
printf("Name: %s\n", name->valuestring);
}
if (cJSON_IsNumber(age)) {
printf("Age: %d\n", age->valueint); // 使用 valueint 获取整数部分
}
if (cJSON_IsBool(is_student)) {
printf("Is Student: %s\n", cJSON_IsTrue(is_student) ? "true" : "false");
}
// 4. 释放内存
cJSON_Delete(root);
}
int main() {
const char *json = "{\"name\":\"Alice\",\"age\":25,\"is_student\":true}";
print_json_object(json);
return 0;
}
说明:
cJSON_GetObjectItemCaseSensitive(parent_object, key_name):返回key_name对应的cJSON*节点,如果找不到或parent_object不是对象,则返回NULL。- 获取到节点后,需要使用
cJSON_IsString()、cJSON_IsNumber()、cJSON_IsBool()等宏判断节点类型,然后通过valuestring、valueint、valuedouble、valueint(用于 true/false, 1 表示 true, 0 表示 false) 等成员获取实际值。 - 务必记得在最后使用
cJSON_Delete(root)释放所有通过cJSON_Parse分配的内存,否则会导致内存泄漏。
遍历对象的所有键值对
如果不知道对象有哪些键,或者需要处理所有键值对,可以使用 cJSON_GetArrayItem() 的“变体”方式,或者更直接地遍历对象的子节点。 cJSON 对象内部维护一个子节点链表。
void traverse_json_object(const char *json_string) {
cJSON *root = cJSON_Parse(json_string);
if (root == NULL) {
fprintf(stderr, "Error parsing JSON.\n");
return;
}
cJSON *current_item = NULL;
cJSON *child = root->child; // 对象的第一个子节点
printf("Traversing JSON object:\n");
while (child != NULL) {
printf("Key: %s, Type: ", child->string);
if (cJSON_IsString(child)) {
printf("String, Value: %s\n", child->valuestring);
} else if (cJSON_IsNumber(child)) {
printf("Number, Value: %d\n", child->valueint);
} else if (cJSON_IsBool(child)) {
printf("Boolean, Value: %s\n", cJSON_IsTrue(child) ? "true" : "false");
}
// 可以添加更多类型判断...
child = child->next; // 移动到下一个兄弟节点
}
cJSON_Delete(root);
}
说明:
- 对象的子节点通过
child指针指向第一个子节点。 - 每个子节点通过
next指针指向下一个兄弟节点,通过prev指针指向前一个兄弟节点。 - 每个子节点的
string成员存储其键名(对于对象成员而言)。
拆分 JSON 数组
当遇到 cJSON_Array 类型的节点时,我们需要按索引访问数组元素。
根据索引获取数组元素
使用 cJSON_GetArrayItem() 函数可以通过索引获取数组中的元素。
void parse_json_array(const char *json_string) {
cJSON *root = cJSON_Parse(json_string);
if (root == NULL) {
fprintf(stderr, "Error parsing JSON.\n");
return;
}
cJSON *fruits_array = cJSON_GetObjectItemCaseSensitive(root, "fruits");
if (cJSON_IsArray(fruits_array)) {
int array_size = cJSON_GetArraySize(fruits_array);
printf("Fruits array size: %d\n", array_size);
printf("Fruits:\n");
for (int i = 0; i < array_size; i++) {
cJSON *fruit_item = cJSON_GetArrayItem(fruits_array, i);
if (cJSON_IsString(fruit_item) && (fruit_item->valuestring != NULL)) {
printf(" %d: %s\n", i + 1, fruit_item->valuestring);
}
}
} else {
printf("fruits is not an array.\n");
}
cJSON_Delete(root);
}
int main() {
const char *json = "{\"name\":\"Bob\",\"fruits\":[\"apple\",\"banana\",\"orange\"]}";
parse_json_array(json);
return 0;
}
说明:
cJSON_GetArrayItem(array, index):返回array中索引为index的cJSON*节点,索引从 0 开始,如果索引越界或array不是数组,则返回NULL。cJSON_GetArraySize(array):获取数组的大小(元素个数)。
遍历数组元素
遍历数组元素通常结合 cJSON_GetArraySize() 和 cJSON_GetArrayItem() 使用,如上例所示,或者,也可以利用数组的子节点链表特性(与对象类似,但数组元素的 string 为 NULL):
void traverse_json_array(const char *json_string) {
cJSON *root = cJSON_Parse(json_string);
if (root == NULL) {
fprintf(stderr, "Error parsing JSON.\n");
return;
}
cJSON *fruits_array = cJSON_GetObjectItemCaseSensitive(root, "fruits");
if (cJSON_IsArray(fruits_array)) {
cJSON *current_item = fruits_array->child; // 数组的第一个元素
int index = 0;
printf("Traversing fruits array:\n");
while (current_item != NULL) {
if (cJSON_IsString(current_item)) {
printf(" Index %d: %s\n", index++, current_item->valuestring);
}
current_item = current_item->next;
}
}
cJSON_Delete(root);
}
拆分嵌套的 JSON 结构
实际的 JSON 数据往往是嵌套的,即对象中包含对象或数组,数组中又包含对象或其他数组,拆分嵌套结构的关键是递归地应用



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