C语言如何将JSON数据转换为结构体对象?
在C语言中处理JSON数据是一项常见但颇具挑战性的任务,由于C语言本身没有内置的JSON解析器和对象反射机制(像C#、Java或Python那样),我们不能像在其他语言中那样轻松地将JSON字符串直接反序列化为一个结构体(class/struct),这需要借助第三方库来实现。
本文将详细介绍在C语言中如何使用流行的第三方库 cJSON 来将JSON数据转换为自定义的结构体对象,cJSON是一个轻量级、高效的C语言JSON解析器,非常适合嵌入式系统和桌面应用。
核心思路
整个过程可以分解为以下几个步骤:
- 包含头文件并初始化cJSON库:在使用cJSON之前,必须包含其头文件,并在程序开始时进行初始化(在较新版本中,此步骤有时是可选的,但养成好习惯总是好的)。
- 解析JSON字符串:使用
cJSON_Parse()函数将JSON格式的字符串解析成一个cJSON对象树。 - 遍历cJSON对象树:根据JSON的结构,通过
cJSON_GetObjectItem()等函数逐层访问数据。 - 提取数据并填充C结构体:从cJSON对象中获取各种类型的数据(字符串、数字、布尔值等),并将其赋值给我们预先定义好的C语言结构体成员。
- 释放内存:cJSON在堆上分配了内存,使用完毕后必须调用
cJSON_Delete()来释放整个cJSON对象树,防止内存泄漏。
实践步骤:一个完整的示例
让我们通过一个具体的例子来演示这个过程,假设我们有以下JSON数据:
{
"name": "John Doe",
"age": 30,
"isStudent": false,
"courses": [
{ "title": "C Programming", "credits": 3 },
{ "title": "Data Structures", "credits": 4 }
],
"address": {
"city": "New York",
"zip": "10001"
}
}
我们的目标是将这个JSON字符串转换为一个C语言的结构体。
第1步:定义C语言结构体
我们需要创建与JSON数据结构相匹配的C语言结构体,这包括处理嵌套对象和数组。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cJSON.h" // 确保cJSON.h在你的项目路径中
// 定义课程结构体
typedef struct {
char title[50];
int credits;
} Course;
// 定义地址结构体
typedef struct {
char city[50];
char zip[20];
} Address;
// 定义主结构体
typedef struct {
char name[50];
int age;
int isStudent; // C语言没有bool,通常用int或char表示
Course* courses; // 动态数组
int courseCount;
Address address;
} Person;
第2步:编写转换函数
我们编写一个核心函数,它接收JSON字符串,并返回填充好的 Person 结构体指针。
Person* createPersonFromJson(const char* jsonString) {
// 1. 解析JSON字符串
cJSON* root = cJSON_Parse(jsonString);
if (root == NULL) {
const char* error_ptr = cJSON_GetErrorPtr();
if (error_ptr) {
fprintf(stderr, "Error before: %s\n", error_ptr);
}
return NULL;
}
// 2. 为Person结构体分配内存
Person* person = (Person*)malloc(sizeof(Person));
if (person == NULL) {
fprintf(stderr, "Failed to allocate memory for Person\n");
cJSON_Delete(root);
return NULL;
}
memset(person, 0, sizeof(Person)); // 初始化为0
// 3. 从cJSON对象中提取数据并填充结构体
// 提取name (字符串)
cJSON* nameItem = cJSON_GetObjectItem(root, "name");
if (cJSON_IsString(nameItem) && (nameItem->valuestring != NULL)) {
strncpy(person->name, nameItem->valuestring, sizeof(person->name) - 1);
person->name[sizeof(person->name) - 1] = '\0'; // 确保字符串终止
}
// 提取age (数字)
cJSON* ageItem = cJSON_GetObjectItem(root, "age");
if (cJSON_IsNumber(ageItem)) {
person->age = ageItem->valueint;
}
// 提取isStudent (布尔值,cJSON中用1和0表示)
cJSON* isStudentItem = cJSON_GetObjectItem(root, "isStudent");
if (cJSON_IsBool(isStudentItem)) {
person->isStudent = cJSON_IsTrue(isStudentItem) ? 1 : 0;
}
// 提取courses (数组)
cJSON* coursesArray = cJSON_GetObjectItem(root, "courses");
if (cJSON_IsArray(coursesArray)) {
person->courseCount = cJSON_GetArraySize(coursesArray);
person->courses = (Course*)malloc(person->courseCount * sizeof(Course));
for (int i = 0; i < person->courseCount; i++) {
cJSON* courseItem = cJSON_GetArrayItem(coursesArray, i);
cJSON* titleItem = cJSON_GetObjectItem(courseItem, "title");
cJSON* creditsItem = cJSON_GetObjectItem(courseItem, "credits");
if (cJSON_IsString(titleItem) && (titleItem->valuestring != NULL)) {
strncpy(person->courses[i].title, titleItem->valuestring, sizeof(person->courses[i].title) - 1);
person->courses[i].title[sizeof(person->courses[i].title) - 1] = '\0';
}
if (cJSON_IsNumber(creditsItem)) {
person->courses[i].credits = creditsItem->valueint;
}
}
}
// 提取address (嵌套对象)
cJSON* addressObject = cJSON_GetObjectItem(root, "address");
if (addressObject != NULL) {
cJSON* cityItem = cJSON_GetObjectItem(addressObject, "city");
cJSON* zipItem = cJSON_GetObjectItem(addressObject, "zip");
if (cJSON_IsString(cityItem) && (cityItem->valuestring != NULL)) {
strncpy(person->address.city, cityItem->valuestring, sizeof(person->address.city) - 1);
person->address.city[sizeof(person->address.city) - 1] = '\0';
}
if (cJSON_IsString(zipItem) && (zipItem->valuestring != NULL)) {
strncpy(person->address.zip, zipItem->valuestring, sizeof(person->address.zip) - 1);
person->address.zip[sizeof(person->address.zip) - 1] = '\0';
}
}
// 4. 释放cJSON对象树
cJSON_Delete(root);
return person;
}
第3步:使用和释放内存
在 main 函数中,我们可以调用这个转换函数,并记得在使用完结构体后释放所有动态分配的内存。
void printPerson(const Person* person) {
printf("Name: %s\n", person->name);
printf("Age: %d\n", person->age);
printf("Is Student: %s\n", person->isStudent ? "Yes" : "No");
printf("Address: %s, %s\n", person->address.city, person->address.zip);
printf("Courses:\n");
for (int i = 0; i < person->courseCount; i++) {
printf(" - %s (%d credits)\n", person->courses[i].title, person->courses[i].credits);
}
printf("\n");
}
void freePerson(Person* person) {
if (person != NULL) {
// 释放courses数组
if (person->courses != NULL) {
free(person->courses);
person->courses = NULL;
}
// 释放Person结构体本身
free(person);
}
}
int main() {
const char* jsonString = "{\n \"name\": \"John Doe\",\n \"age\": 30,\n \"isStudent\": false,\n \"courses\": [\n { \"title\": \"C Programming\", \"credits\": 3 },\n { \"title\": \"Data Structures\", \"credits\": 4 }\n ],\n \"address\": {\n \"city\": \"New York\",\n \"zip\": \"10001\"\n }\n}";
Person* person = createPersonFromJson(jsonString);
if (person != NULL) {
printPerson(person);
freePerson(person);
}
return 0;
}
第4步:编译与链接
要编译这个程序,你需要:
- 获取
cJSON库的源代码(cJSON.c和cJSON.h)。 - 将它们放在你的项目目录中。



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