JSON (JavaScript Object Notation)
is a lightweight data interchange format. JSON is widely used in internet-related development, and it is also quite common in embedded systems. Recently, I used it in a project, and I would like to share my experience.
An example of a simple JSON formatted data is as follows:
{
"name": "xxx",
"num": xxx,
"c_score": xxx
}
Here, we need to understand a concept: key-value pair
. For example:
"name": "xxx"
Such a structure is a key-value pair.
As the sender, we need to combine useful data like xxx into JSON format to send to the receiver; as the receiver, we need to parse the useful data from this JSON data.
For simple JSON data, we can use some string manipulation library functions in C to package and parse it, but for slightly more complex JSON, it may not be so easy to handle.
At this time, we can use a third-party library – cJSON
, which makes data packaging and parsing very convenient.
cJSON library repository address:
https://github.com/DaveGamble/cJSON.git
Or:
https://gitee.com/mirrors/cJSON.git
Next, I will share an example of using the cJSON library for data packaging and parsing.
1. Packaging and Parsing Example
1. Determine Protocol Data
In actual development, to use JSON data for communication, we must first determine what data both parties will exchange. If necessary, a protocol document should also be written. The protocol document includes the data to be transmitted, data types, and other information.
For example:
2. Example of Packaging JSON Data
Input some student information from the console, combine it into a string formatted JSON data packet, and then output it to the console.
Operation Example:
First, we download the cJSON
source code from the repository, the folder contents are as follows:
We only need to copy the cJSON.c
and cJSON.h
files to the root directory of our project to use them, for example:
From cJSON.h
, we can see that it provides us with many interfaces:
In this example, we only need to focus on the following interfaces:
cJSON_CreateObject: Create a JSON object, enclosed in { }
cJSON_CreateString: Create a string
cJSON_CreateNumber: Create int type data
cJSON_AddItemToObject: Add to JSON object
cJSON_Print: Present in standard JSON format
cJSON_PrintUnformatted: Present in JSON format without spaces
cJSON_Delete: Delete JSON object to free memory
The packaging function we created is as follows:
Swipe left and right to view all code >>>
static char *StudentsData_Packet(pStudentDef _Stu)
{
char *res_string = NULL; // Return value
cJSON *name = NULL; // Name
cJSON *num = NULL; // Student number
cJSON *c_score = NULL; // C language score
/* Create a JSON object, enclosed in { } */
cJSON *obj = cJSON_CreateObject();
if (obj == NULL)
{
goto end;
}
/* Create the key-value pair "name": "xxx" */
name = cJSON_CreateString(_Stu->name);
if (name == NULL)
{
goto end;
}
cJSON_AddItemToObject(obj, "name", name);
/* Create the key-value pair "num": 207 */
num = cJSON_CreateNumber(_Stu->num);
if (name == NULL)
{
goto end;
}
cJSON_AddItemToObject(obj, "num", num);
/* Create the key-value pair "c_score": 95 */
c_score = cJSON_CreateNumber(_Stu->c_score);
if (name == NULL)
{
goto end;
}
cJSON_AddItemToObject(obj, "c_score", c_score);
res_string = cJSON_Print(obj); // Present in JSON format
// res_string = cJSON_PrintUnformatted(obj); // Present in unformatted JSON
if (res_string == NULL)
{
fprintf(stderr, "Failed to print monitor.\n");
}
/* Handle exceptions uniformly with Delete (free) */
end:
cJSON_Delete(obj);
return res_string;
}
For detailed explanations, see the comments. Let’s focus on cJSON_Print
and cJSON_PrintUnformatted
among these interfaces.
The difference between these two interfaces is whether the combined JSON data contains spaces. We can see the difference through online JSON-related websites:
https://www.sojson.com/json/json_online.html
The JSON data with spaces, when using cJSON_Print
, looks like this:
The JSON data without spaces, when using cJSON_PrintUnformatted
, looks like this:
If you want to output for viewing, using cJSON_Print
is more convenient; if it’s for actual communication, then using cJSON_PrintUnformatted
is better, as it reduces the communication load by removing spaces.
Complete Code:
Swipe left and right to view all code >>>
/*
Author: ZhengN
Public Account: Embedded Miscellaneous
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cJSON.h"
#define STU_NAME_LEN 32
/* Student structure */
typedef struct _Student
{
char name[STU_NAME_LEN]; // Name
int num; // Student number
int c_score; // C language score
}StudentDef, *pStudentDef;
/* Internal function declaration */
static char *StudentsData_Packet(pStudentDef _Stu);
/********************************************************************************************************
** Function: main
**------------------------------------------------------------------------------------------------------
** Parameters:
** Description:
** Returns:
********************************************************************************************************/
int main(void)
{
char name[STU_NAME_LEN] = {0};
int num = 0;
int c_score = 0;
StudentDef stu;
int stu_count = 0;
int i = 0;
/* Total number of students */
printf("Please input number of student: ");
scanf("%d", &stu_count);
while (i++ < stu_count)
{
/* Name */
printf("Please input name: ");
scanf("%s", name);
if (strlen(name) < STU_NAME_LEN)
{
strncpy((char*)&stu.name, name, strlen(name)+1);
}
else
{
printf("The name is too long\n");
}
/* Student number */
printf("Please input num (0~100): ");
scanf("%d", &num);
stu.num = num;
/* C language score */
printf("Please input c_score (0~100): ");
scanf("%d", &c_score);
stu.c_score = c_score;
/* Output JSON formatted student data */
printf("%s\n", StudentsData_Packet(&stu));
}
return 0;
}
/********************************************************************************************************
** Function: StudentsData_Packet, Student JSON formatted data packaging
**------------------------------------------------------------------------------------------------------
** Parameters: _Stu: Data needed to package student json data
** Description:
** Returns: JSON formatted string
********************************************************************************************************/
static char *StudentsData_Packet(pStudentDef _Stu)
{
char *res_string = NULL; // Return value
cJSON *name = NULL; // Name
cJSON *num = NULL; // Student number
cJSON *c_score = NULL; // C language score
/* Create a JSON object, enclosed in { } */
cJSON *obj = cJSON_CreateObject();
if (obj == NULL)
{
goto end;
}
/* Create the key-value pair "name": "xxx" */
name = cJSON_CreateString(_Stu->name);
if (name == NULL)
{
goto end;
}
cJSON_AddItemToObject(obj, "name", name);
/* Create the key-value pair "num": 207 */
num = cJSON_CreateNumber(_Stu->num);
if (name == NULL)
{
goto end;
}
cJSON_AddItemToObject(obj, "num", num);
/* Create the key-value pair "c_score": 95 */
c_score = cJSON_CreateNumber(_Stu->c_score);
if (name == NULL)
{
goto end;
}
cJSON_AddItemToObject(obj, "c_score", c_score);
res_string = cJSON_Print(obj); // Present in JSON format
// res_string = cJSON_PrintUnformatted(obj); // Present in unformatted JSON
if (res_string == NULL)
{
fprintf(stderr, "Failed to print monitor.\n");
}
/* Handle exceptions uniformly with Delete (free) */
end:
cJSON_Delete(obj);
return res_string;
}
For detailed explanations, see the comments. Let’s focus on cJSON_Print
and cJSON_PrintUnformatted
among these interfaces.
The difference between these two interfaces is whether the combined JSON data contains spaces. We can see the difference through online JSON-related websites:
https://www.sojson.com/json/json_online.html
The JSON data with spaces, when using cJSON_Print
, looks like this:
The JSON data without spaces, when using cJSON_PrintUnformatted
, looks like this:
If you want to output for viewing, using cJSON_Print
is more convenient; if it’s for actual communication, then using cJSON_PrintUnformatted
is better, as it reduces the communication load by removing spaces.
Complete Code:
Swipe left and right to view all code >>>
/*
Author: ZhengN
Public Account: Embedded Miscellaneous
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>
#include "cJSON.h"
#define STU_NAME_LEN 32
/* Student structure */
typedef struct _Student
{
char name[STU_NAME_LEN]; // Name
int num; // Student number
int c_score; // C language score
}StudentDef, *pStudentDef;
/* Internal function declaration */
static StudentDef StudentData_Prepare(void);
static char *StudentsData_Packet(pStudentDef _Stu);
static void StudentData_Send(const char *_data);
/********************************************************************************************************
** Function: main
**------------------------------------------------------------------------------------------------------
** Parameters:
** Description:
** Returns:
********************************************************************************************************/
int main(void)
{
StudentDef stu = {0};
char *stu_data = NULL;
int stu_count = 0;
int i = 0;
/* Total number of students to register */
printf("Please input number of student: ");
scanf("%d", &stu_count);
while (i++ < stu_count)
{
/* Prepare data */
stu = StudentData_Prepare();
/* JSON formatted data packaging */
stu_data = StudentsData_Packet(&stu);
/* Send data */
StudentData_Send(stu_data);
}
return 0;
}
/********************************************************************************************************
** Function: StudentData_Prepare, Prepare data needed for packaging
**------------------------------------------------------------------------------------------------------
** Parameters:
** Description:
** Returns: The obtained data
********************************************************************************************************/
static StudentDef StudentData_Prepare(void)
{
char name[STU_NAME_LEN] = {0};
int num = 0;
int c_score = 0;
StudentDef stu;
/* Name */
printf("Please input name: ");
scanf("%s", name);
if (strlen(name) < STU_NAME_LEN)
{
strncpy((char*)&stu.name, name, strlen(name)+1);
}
else
{
printf("The name is too long\n");
}
/* Student number */
printf("Please input num (0~100): ");
scanf("%d", &num);
stu.num = num;
/* C language score */
printf("Please input c_score (0~100): ");
scanf("%d", &c_score);
stu.c_score = c_score;
return stu;
}
/********************************************************************************************************
** Function: StudentsData_Packet, JSON formatted data packaging
**------------------------------------------------------------------------------------------------------
** Parameters: _Stu: Data needed to package student json data
** Description:
** Returns: JSON formatted string
********************************************************************************************************/
static char *StudentsData_Packet(pStudentDef _Stu)
{
char *res_string = NULL; // Return value
cJSON *name = NULL; // Name
cJSON *num = NULL; // Student number
cJSON *c_score = NULL; // C language score
/* Create a JSON object, enclosed in { } */
cJSON *obj = cJSON_CreateObject();
if (obj == NULL)
{
goto end;
}
/* Create the key-value pair "name": "xxx" */
name = cJSON_CreateString(_Stu->name);
if (name == NULL)
{
goto end;
}
cJSON_AddItemToObject(obj, "name", name);
/* Create the key-value pair "num": 207 */
num = cJSON_CreateNumber(_Stu->num);
if (name == NULL)
{
goto end;
}
cJSON_AddItemToObject(obj, "num", num);
/* Create the key-value pair "c_score": 95 */
c_score = cJSON_CreateNumber(_Stu->c_score);
if (name == NULL)
{
goto end;
}
cJSON_AddItemToObject(obj, "c_score", c_score);
res_string = cJSON_Print(obj); // Present in JSON format
// res_string = cJSON_PrintUnformatted(obj); // Present in unformatted JSON
if (res_string == NULL)
{
fprintf(stderr, "Failed to print monitor.\n");
}
/* Handle exceptions uniformly with Delete (free) */
end:
cJSON_Delete(obj);
return res_string;
}
/********************************************************************************************************
** Function: StudentData_Send, Send JSON formatted string data
**------------------------------------------------------------------------------------------------------
** Parameters: _data: Data to be sent
** Description:
** Returns:
********************************************************************************************************/
static void StudentData_Send(const char *_data)
{
WSADATA wd;
SOCKET ClientSock;
SOCKADDR_IN ServerSockAddr;
printf("%s\n\n", _data);
/* Initialize the DLL required for sock operations */
WSAStartup(MAKEWORD(2,2),&wd);
/* Request to the server */
memset(&ServerSockAddr, 0, sizeof(ServerSockAddr));
ServerSockAddr.sin_family = AF_INET;
ServerSockAddr.sin_addr.s_addr = inet_addr("127.0.0.1");
ServerSockAddr.sin_port = htons(1314);
/* Create client socket */
if (-1 == (ClientSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)))
{
printf("socket error!\n");
exit(EXIT_FAILURE);
}
if (-1 == connect(ClientSock, (SOCKADDR*)&ServerSockAddr, sizeof(SOCKADDR)))
{
printf("connect error!\n");
exit(EXIT_FAILURE);
}
/* Send data to server */
send(ClientSock, _data, strlen(_data), 0);
/* Close socket */
closesocket(ClientSock);
}
json_parse.c Complete Code:
Swipe left and right to view all code >>>
/*
Author: ZhengN
Public Account: Embedded Miscellaneous
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>
#include "cJSON.h"
#define STU_NAME_LEN 32
/* Student structure */
typedef struct _Student
{
char name[STU_NAME_LEN]; // Name
int num; // Student number
int c_score; // C language score
}StudentDef, *pStudentDef;
/* Internal function declaration */
static char *StudentsData_Recv(void);
static void StudentsData_Parse(pStudentDef _Stu, const char *_JsonStudnetData);
static void PrintParseResult(const pStudentDef _Stu);
static void SaveParseResult(const pStudentDef _Stu);
/* Internal global variables */
static FILE *stu_fp = NULL;
/********************************************************************************************************
** Function: main
**------------------------------------------------------------------------------------------------------
** Parameters:
** Description:
** Returns:
********************************************************************************************************/
int main(void)
{
StudentDef stu = {0};
char *recv_data;
while (1)
{
/* Receive data */
recv_data = StudentsData_Recv();
/* Parse */
StudentsData_Parse(&stu, (const char*)recv_data);
/* Print output parse result */
PrintParseResult(&stu);
/* Save data to file */
SaveParseResult(&stu);
/* Free memory */
free(recv_data); // Prevent memory leak
recv_data = NULL; // Prevent dangling pointer
}
return 0;
}
/********************************************************************************************************
** Function: StudentsData_Recv, Receive data
**------------------------------------------------------------------------------------------------------
** Parameters:
** Description:
** Returns:
********************************************************************************************************/
static char *StudentsData_Recv(void)
{
WSADATA wd;
SOCKADDR_IN ServerSockAddr;
int recv_len = 0;
char *recv_buf = (char*)malloc(512);
static SOCKET ServerSock, ClientSock;
static SOCKADDR ClientAddr;
static int addr_size = 0;
static int run_count = 0;
/* The following operations only need to be executed once */
if (0 == run_count)
{
/* Initialize the DLL required for sock operations */
WSAStartup(MAKEWORD(2,2),&wd);
/* Create server socket */
if (-1 == (ServerSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)))
{
printf("server socket error!\n");
exit(EXIT_FAILURE);
}
/* Set server information */
memset(&ServerSockAddr, 0, sizeof(ServerSockAddr)); // Clear the ServerSockAddr structure
ServerSockAddr.sin_family = AF_INET; // Use IPv4 address
ServerSockAddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // Local IP address
ServerSockAddr.sin_port = htons(1314); // Port
/* Bind socket */
if (-1 == bind(ServerSock, (SOCKADDR*)&ServerSockAddr, sizeof(SOCKADDR)))
{
printf("bind error!\n");
exit(EXIT_FAILURE);
}
printf("bind ok!\n");
/* Enter listening state */
if (-1 == listen(ServerSock, 10))
{
printf("listen error!\n");
exit(EXIT_FAILURE);
}
printf("listen ok!\n");
addr_size = sizeof(SOCKADDR);
}
run_count++;
/* Listen for client requests, the accept function returns a new socket, which is used for sending and receiving */
if (-1 == (ClientSock = accept(ServerSock, (SOCKADDR*)&ClientAddr, &addr_size)))
{
printf("client socket error!\n");
exit(EXIT_FAILURE);
}
/* Accept the returned data from the client */
memset(recv_buf, 0, 512);
recv_len = recv(ClientSock, recv_buf, 512, 0);
printf("%s\n", recv_buf);
/* Close client socket */
closesocket(ClientSock);
/* Return the obtained JSON data */
return (char*)recv_buf;
}
/********************************************************************************************************
** Function: StudentsData_Parse, JSON formatted student data parsing
**------------------------------------------------------------------------------------------------------
** Parameters: _JsonStudnetData: JSON data _Stu: Store the useful data parsed
** Description:
** Returns:
********************************************************************************************************/
static void StudentsData_Parse(pStudentDef _Stu, const char *_JsonStudnetData)
{
cJSON *student_json = NULL; // student_json operation object, can represent the content enclosed in { }
cJSON *name = NULL;
cJSON *num = NULL;
cJSON *c_score = NULL;
/* Start parsing */
student_json = cJSON_Parse(_JsonStudnetData);
if (NULL == student_json)
{
const char *error_ptr = cJSON_GetErrorPtr();
if (error_ptr != NULL)
{
fprintf(stderr, "Error before: %s\n", error_ptr);
}
goto end;
}
/* Parse to get the value of name */
name = cJSON_GetObjectItemCaseSensitive(student_json, "name");
if (cJSON_IsString(name) && (name->valuestring != NULL))
{
memcpy(&_Stu->name, name->valuestring, strlen(name->valuestring));
}
/* Parse to get the value of num */
num = cJSON_GetObjectItemCaseSensitive(student_json, "num");
if (cJSON_IsNumber(num))
{
_Stu->num = num->valueint;
}
/* Parse to get the value of c_score */
c_score = cJSON_GetObjectItemCaseSensitive(student_json, "c_score");
if (cJSON_IsNumber(c_score))
{
_Stu->c_score = c_score->valueint;
}
end:
cJSON_Delete(student_json);
}
/********************************************************************************************************
** Function: PrintParseResult, Print output parse result
**------------------------------------------------------------------------------------------------------
** Parameters:
** Description:
** Returns:
********************************************************************************************************/
static void PrintParseResult(const pStudentDef _Stu)
{
printf("name: %s, num: %d, c_score: %d\n", _Stu->name, _Stu->num, _Stu->c_score);
}
/********************************************************************************************************
** Function: SaveParseResult, Save parse result
**------------------------------------------------------------------------------------------------------
** Parameters: _Stu: Data to be saved
** Description:
** Returns:
********************************************************************************************************/
static void SaveParseResult(const pStudentDef _Stu)
{
char write_buf[512] = {0};
static int stu_count = 0;
/* Open file in append mode */
if((stu_fp = fopen("ParseResult.txt", "a+")) == NULL)
{
printf("Open file error!\n");
return exit(EXIT_FAILURE);
}
/* Write into file in specified format */
snprintf(write_buf, 512, "name: %s, num: %d, c_score: %d\n", _Stu->name, _Stu->num, _Stu->c_score);
size_t len = fwrite((char*)write_buf, 1, strlen(write_buf), stu_fp);
/* Offset file position pointer */
fseek(stu_fp, len * stu_count, SEEK_SET);
stu_count++;
/* Close file */
fclose(stu_fp);
}
Compilation commands:
Swipe left and right to view all code >>>
gcc json_print.c cJSON.c -o json_print.exe -lwsocket32
gcc json_parse.c cJSON.c -o json_parse.exe -lwsocket32
The comprehensive demo adds socket-related code, and this note mainly introduces JSON data packaging and parsing. Regarding socket-related content, no further explanation will be given. Interested friends can read previous notes related to sockets:
[Socket Notes] Summary of TCP and UDP Communication
[Socket Application] Implementation of Weather Client Based on C Language
Using Quectel 4G Module Dial-Up + Socket to Obtain Weather Data

END
→ Follow to avoid getting lost ←