C语言解析JSON:实用指南与代码示例**
JSON(JavaScript Object Notation)因其轻量级、易读易写的特性,已成为现代应用程序中数据交换的主流格式之一,在C语言这种更接近系统底层的语言中处理JSON数据,虽然不像一些高级语言那样内置便捷的JSON解析库,但通过选择合适的第三方库,依然可以高效地实现,本文将详细介绍如何在C语言中解析JSON,包括常用库的选择、基本使用方法以及一个完整的示例。
为什么在C中解析JSON需要库?
C语言本身没有内置对JSON数据类型的直接支持,JSON中的对象、数组、字符串、数字等类型都需要在C中用相应的数据结构(如结构体、数组、字符串等)来表示,手动解析JSON字符串不仅复杂,容易出错,还需要处理各种边界情况(如转义字符、嵌套结构等),使用成熟的第三方JSON解析库是推荐的做法,它们能简化开发,提高代码的健壮性和可维护性。
常用的C语言JSON解析库
-
cJSON:
- 特点:轻量级、单文件(只有一个
cJSON.h和cJSON.c)、易于集成、API简单直观、性能较好。 - 适用场景:大多数需要JSON解析的C项目,特别是对资源占用有一定要求的嵌入式系统或小型应用。
- GitHub地址:https://github.com/DaveGamble/cJSON
- 特点:轻量级、单文件(只有一个
-
Jansson:
- 特点:功能丰富、支持JSON的完整数据类型、类型安全、提供详细的错误报告、内存管理清晰。
- 适用场景:对JSON功能要求较高、需要复杂错误处理的中大型项目。
- 官网/GitHub:https://github.com/akheron/jansson
-
Yajl (Yet Another JSON Library):
- 特点:流式解析器(SAX风格),内存占用极低,适合处理大型JSON文件或数据流。
- 适用场景:需要解析超大JSON文件、网络数据流等对内存敏感的场景。
- GitHub地址:https://github.com/lloyd/yajl
对于初学者和大多数应用场景,cJSON 是一个绝佳的选择,本文将以cJSON为例进行讲解。
使用cJSON解析JSON的基本步骤
获取并集成cJSON
最简单的方式是直接从GitHub下载cJSON.h和cJSON.c文件,将它们添加到你的项目中,如果你使用包管理器(如vcpkg、conan等),也可以通过它们来安装cJSON。
包含头文件
在你的C源文件中,包含cJSON的头文件:
#include "cJSON.h"
解析JSON字符串
使用cJSON_Parse()函数将JSON格式的字符串解析成一个cJSON对象指针。
cJSON *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 -1;
}
注意:cJSON_Parse()会为解析出的JSON对象分配内存,使用完毕后需要释放。
访问JSON数据
cJSON提供了多种函数来访问和遍历JSON数据结构:
-
获取对象中的值:
cJSON_GetObjectItemCaseSensitive():获取对象中指定键(区分大小写)的值,返回一个cJSON*指针。cJSON_GetObjectItem():获取对象中指定键(不区分大小写)的值。cJSON *name = cJSON_GetObjectItemCaseSensitive(root, "name"); if (cJSON_IsString(name)) { printf("Name: %s\n", name->valuestring); }
-
判断值的类型:
cJSON_IsString()、cJSON_IsNumber()、cJSON_IsObject()、cJSON_IsArray()、cJSON_IsTrue()、cJSON_IsFalse()、cJSON_IsNull()等。cJSON *age = cJSON_GetObjectItemCaseSensitive(root, "age"); if (cJSON_IsNumber(age)) { printf("Age: %d\n", age->valueint); // 或 age->valuedouble }
-
遍历数组:
cJSON *hobbies = cJSON_GetObjectItemCaseSensitive(root, "hobbies"); if (cJSON_IsArray(hobbies)) { int hobby_count = cJSON_GetArraySize(hobbies); printf("Hobbies (%d): \n", hobby_count); for (int i = 0; i < hobby_count; i++) { cJSON *hobby = cJSON_GetArrayItem(hobbies, i); if (cJSON_IsString(hobby)) { printf(" - %s\n", hobby->valuestring); } } } -
嵌套访问: JSON对象可以嵌套,只需逐层获取即可。
cJSON *address = cJSON_GetObjectItemCaseSensitive(root, "address"); if (cJSON_IsObject(address)) { cJSON *city = cJSON_GetObjectItemCaseSensitive(address, "city"); if (cJSON_IsString(city)) { printf("City: %s\n", city->valuestring); } }
释放内存
非常重要的一步!使用cJSON_Delete()函数释放整个JSON对象树所占用的内存,避免内存泄漏。
cJSON_Delete(root);
完整示例代码
假设我们有以下JSON字符串:
{
"name": "张三",
"age": 30,
"isStudent": false,
"address": {
"city": "北京",
"street": "长安街"
},
"hobbies": ["编程", "阅读", "旅游"]
}
下面是一个使用cJSON解析这个字符串的完整C程序:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cJSON.h" // 确保cJSON.h和cJSON.c在同一目录或正确包含路径
void parse_json_example(const char *json_string) {
cJSON *root = NULL;
cJSON *name = NULL;
cJSON *age = NULL;
cJSON *isStudent = NULL;
cJSON *address = NULL;
cJSON *city = NULL;
cJSON *street = NULL;
cJSON *hobbies = NULL;
cJSON *hobby = NULL;
int hobby_count = 0;
int i = 0;
// 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. 获取并打印name
name = cJSON_GetObjectItemCaseSensitive(root, "name");
if (cJSON_IsString(name) && (name->valuestring != NULL)) {
printf("姓名: %s\n", name->valuestring);
}
// 3. 获取并打印age
age = cJSON_GetObjectItemCaseSensitive(root, "age");
if (cJSON_IsNumber(age)) {
printf("年龄: %d\n", age->valueint);
}
// 4. 获取并打印isStudent
isStudent = cJSON_GetObjectItemCaseSensitive(root, "isStudent");
if (cJSON_IsFalse(isStudent)) {
printf("是否学生: 否\n");
} else if (cJSON_IsTrue(isStudent)) {
printf("是否学生: 是\n");
}
// 5. 获取并打印address(嵌套对象)
address = cJSON_GetObjectItemCaseSensitive(root, "address");
if (cJSON_IsObject(address)) {
city = cJSON_GetObjectItemCaseSensitive(address, "city");
street = cJSON_GetObjectItemCaseSensitive(address, "street");
if (cJSON_IsString(city) && (city->valuestring != NULL)) {
printf("城市: %s\n", city->valuestring);
}
if (cJSON_IsString(street) && (street->valuestring != NULL)) {
printf("街道: %s\n", street->valuestring);
}
}
// 6. 获取并打印hobbies(数组)
hobbies = cJSON_GetObjectItemCaseSensitive(root, "hobbies");
if (cJSON_IsArray(hobbies)) {
hobby_count = cJSON_GetArraySize(hobbies);
printf("爱好 (%d): \n", hobby_count);
for (i = 0; i < hobby_count; i++) {
hobby = cJSON_GetArrayItem(hobbies


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