C语言中JSON通讯的实现方法与实践
在跨平台、跨语言的系统开发中,JSON(JavaScript Object Notation)因其轻量级、易读性强、解析效率高的特点,已成为数据交换的主流格式之一,C语言作为系统级编程的基础语言,常用于后端服务、嵌入式开发等场景,实现JSON格式的数据通讯是许多项目的核心需求,本文将详细介绍C语言中JSON通讯的实现原理、关键步骤及常用工具,并通过代码示例展示完整流程。
JSON通讯的核心原理
JSON通讯的本质是数据的序列化(编码)与反序列化(解码):
- 序列化:将C语言中的数据结构(如结构体、数组、基本类型)转换为JSON格式的字符串,便于网络传输或存储。
- 反序列化:将接收到的JSON字符串解析为C语言可识别的数据结构,供程序逻辑处理。
完整的JSON通讯流程通常包括:
- 数据封装:在发送端,将业务数据按JSON格式序列化为字符串;
- 数据传输:通过TCP/IP、HTTP、WebSocket等协议传输JSON字符串;
- 数据解析:在接收端,解析JSON字符串并还原为原始数据结构;
- 业务处理:基于解析后的数据进行后续逻辑操作。
选择合适的JSON库
C语言没有内置JSON支持,需依赖第三方库实现序列化与反序列化,以下是几款主流的JSON库及其特点:
cJSON
- 特点:轻量级、单文件实现(仅
cJSON.h和cJSON.c)、API简单、无需依赖其他库。 - 适用场景:对资源占用敏感的嵌入式系统或小型项目。
- GitHub地址:https://github.com/DaveGamble/cJSON
Jansson
- 特点:功能完善(支持JSON标准全类型)、内存管理自动化、性能较好。
- 适用场景:中大型项目,需要稳定性和高级功能(如JSON路径查询)。
- 官网:https://github.com/akheron/jansson
Parson
- 特点:轻量级、跨平台、API设计直观,支持流式解析(适合大JSON文件)。
- 适用场景:需要平衡性能与易用性的项目。
- GitHub地址:https://github.com/kgabis/parson
本文以cJSON为例,因其“开箱即用”的特性,适合初学者快速理解JSON通讯的实现。
基于cJSON的JSON通讯实现步骤
环境准备
下载cJSON源码(或通过包管理器安装,如apt-get install libcjson-dev),将cJSON.h和cJSON.c添加到项目中,编译时链接cJSON库:
gcc -o app app.c cJSON.c -lm
数据序列化:将C数据转为JSON字符串
假设需要发送一个用户信息数据,包含用户ID、姓名、年龄和爱好列表,定义C结构体如下:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cJSON.h"
typedef struct {
int id;
char name[50];
int age;
char *hobbies[3]; // 动态数组存储爱好
} UserInfo;
通过cJSON的API构建JSON对象并序列化:
char* serializeUserInfo(const UserInfo *user) {
// 1. 创建JSON对象(对应JSON的{})
cJSON *root = cJSON_CreateObject();
if (!root) {
fprintf(stderr, "Failed to create JSON object\n");
return NULL;
}
// 2. 添加键值对(cJSON_AddNumber/String/Array等)
cJSON_AddNumberToObject(root, "id", user->id);
cJSON_AddStringToObject(root, "name", user->name);
cJSON_AddNumberToObject(root, "age", user->age);
// 3. 添加爱好数组(对应JSON的[])
cJSON *hobbiesArray = cJSON_CreateArray();
for (int i = 0; i < 3; i++) {
if (user->hobbies[i] != NULL) {
cJSON_AddItemToArray(hobbiesArray, cJSON_CreateString(user->hobbies[i]));
}
}
cJSON_AddItemToObject(root, "hobbies", hobbiesArray);
// 4. 序列化为字符串(需手动释放)
char *jsonStr = cJSON_Print(root);
if (!jsonStr) {
fprintf(stderr, "Failed to serialize JSON\n");
cJSON_Delete(root);
return NULL;
}
// 5. 释放JSON对象(避免内存泄漏)
cJSON_Delete(root);
return jsonStr;
}
调用示例:
int main() {
UserInfo user = {
.id = 1001,
.name = "Alice",
.age = 25,
.hobbies = {"reading", "swimming", "coding"}
};
char *jsonStr = serializeUserInfo(&user);
if (jsonStr) {
printf("Serialized JSON: %s\n", jsonStr);
free(jsonStr); // 释放序列化字符串
}
return 0;
}
输出结果:
{"id":1001,"name":"Alice","age":25,"hobbies":["reading","swimming","coding"]}
数据反序列化:将JSON字符串转为C数据
假设接收端收到上述JSON字符串,需解析为UserInfo结构体:
int deserializeUserInfo(const char *jsonStr, UserInfo *user) {
// 1. 解析JSON字符串
cJSON *root = cJSON_Parse(jsonStr);
if (!root) {
fprintf(stderr, "Failed to parse JSON: %s\n", cJSON_GetErrorPtr());
return -1;
}
// 2. 获取键值对(cJSON_GetObjectItem等)
cJSON *idItem = cJSON_GetObjectItem(root, "id");
cJSON *nameItem = cJSON_GetObjectItem(root, "name");
cJSON *ageItem = cJSON_GetObjectItem(root, "age");
cJSON *hobbiesArray = cJSON_GetObjectItem(root, "hobbies");
if (!idItem || !nameItem || !ageItem || !hobbiesArray) {
fprintf(stderr, "Missing required fields in JSON\n");
cJSON_Delete(root);
return -1;
}
// 3. 填充C结构体
user->id = idItem->valueint;
strncpy(user->name, nameItem->valuestring, sizeof(user->name) - 1);
user->age = ageItem->valueint;
// 4. 解析数组
int hobbyCount = cJSON_GetArraySize(hobbiesArray);
for (int i = 0; i < hobbyCount && i < 3; i++) {
cJSON *hobbyItem = cJSON_GetArrayItem(hobbiesArray, i);
if (hobbyItem && hobbyItem->valuestring) {
user->hobbies[i] = strdup(hobbyItem->valuestring); // 需手动释放
}
}
// 5. 释放JSON对象
cJSON_Delete(root);
return 0;
}
调用示例:
int main() {
const char *jsonStr = "{\"id\":1001,\"name\":\"Alice\",\"age\":25,\"hobbies\":[\"reading\",\"swimming\",\"coding\"]}";
UserInfo user;
if (deserializeUserInfo(jsonStr, &user) == 0) {
printf("Deserialized UserInfo:\n");
printf("ID: %d\n", user.id);
printf("Name: %s\n", user.name);
printf("Age: %d\n", user.age);
printf("Hobbies: %s, %s, %s\n", user.hobbies[0], user.hobbies[1], user.hobbies[2]);
// 释放动态分配的字符串
for (int i = 0; i < 3; i++) {
if (user.hobbies[i]) free(user.hobbies[i]);
}
}
return 0;
}
网络传输:基于TCP的JSON通讯
JSON字符串本身是文本格式,可直接通过TCP套接字传输,以下是简单的客户端-服务器通讯示例:
服务器端(接收并解析JSON)
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <arpa/inet.h>
#define PORT 8080
#define BUFFER_SIZE 1024
int main() {
int server_fd, new_socket;
struct sockaddr_in address;
int opt = 1;
int addrlen = sizeof(address);
char buffer[BUFFER_SIZE] = {0};
// 1. 创建socket
if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
perror("socket failed");
exit(EXIT_FAILURE);
}
// 2. 绑定端口
if (setsockopt(server


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