c json解析/创建详解及代码示例
1. JSON 定义
JSON (JavaScript Object Notation) is a lightweight data-interchange format.
It is easy for humans to read and write. It is easy for machines to parse and generate.
2. json 介绍及语法
2.1 json 介绍
JSON: JavaScript Object Notation(JavaScript 对象表示法)
JSON 是轻量级的文本数据交换格式
2.2 json 语法
数据在名称/值对中,key : value
数据由逗号分隔
大括号 {} 保存对象
中括号 [] 保存数组,数组可以包含多个对象
2.2.1 JSON key 键
JSON key 必须是字符串
2.2.2 JSON value 值
JSON 值可以是:字符串, 数字, 对象, 数组, 布尔值或 null
-
数字(整数或浮点数)
JSON 数字可以是整型或者浮点型:{ “age”:30 }
-
字符串(在双引号中)
{ “name”:“hani”}
-
逻辑值(true 或 false)
JSON 布尔值可以是 true 或者 false:{ “flag”:true }
-
数组(在中括号[]中)
数组值必须是合法的 JSON 数据类型(字符串, 数字, 对象, 数组, 布尔值或 null)。
如:{
“name”:“hani”,
“age”:18,
“sites”:[ “Google”, “baidu”, “google” ]
}
数组可包含多个对象:
{
"sites": [
{ "name":"hani" , "url":"www.hani.com" },
{ "name":"google" , "url":"www.google.com" },
{ "name":"baidu" , "url":"www.baidu.com" }
]
}
- 对象(在大括号{}中)
JSON 对象使用在大括号({})中书写。
对象可以包含多个 key/value(键/值)对。
key 必须是字符串,value 可以是合法的 JSON 数据类型(字符串, 数字, 对象, 数组, 布尔值或 null)。
key 和 value 中使用冒号(:)分割。
每个 key/value 对使用逗号(,)分割。
JSON 对象在大括号 {} 中书写:
{key1 : value1, key2 : value2, ... keyN : valueN }
对象可以包含多个名称/值对:
{ "name":"hani" , "url":"www.hani.com" }
-
null
JSON 可以设置 null 值:{ “runoob”:null }
3. cJSON 的核心结构体 cJSON
typedef struct cJSON {
struct cJSON*next,*prev; /* 遍历数组或对象链的前向或后向链表指针*/
struct cJSON *child; /*数组或对象的孩子节点*/
int type; /* key的类型*/
char *valuestring; /*字符串值*/
int valueint; /* 整数值*/
double valuedouble; /* 浮点数值*/
char *string; /* key的名字*/
} cJSON;
3.1 cJSON 说明:
1、cJSON是使用链表来存储数据的,其访问方式很像一颗树。每一个节点可以有兄弟节点,通过next/prev指针来查找,它类似双向链表;每个节点也可以有孩子节点,通过child指针来访问,进入下一层。只有节点是对象或数组时才可以有孩子节点。
3.2 cjson 源码 github
https://github.com/DaveGamble/cJSON
只需要 cJSON.c cJSON.h 两个文件即可
4. 代码示例-解析json
4.1. json 示例文件: test.txt
{
"filename": "test.tar",
"fileid": "888",
"port": 1024,
"subfile": [{
"subfileName": "aaa.txt",
"size": 102.00,
"author": [{
"name": "hani"
}, {
"name": "lufei"
}]
}, {
"subfileName": "bbb.docx",
"size": 560.00,
"author": [{
"name": "suolong"
}, {
"name": "namei"
}]
}]
}
4.2 解析json 主函数: jsonTest.c
说明: 解析json中的数组有两种方法:
方法一: cJSON_GetArraySize + for
方法二: cJSON_ArrayForEach
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include "cJSON.h"
/* parseJson : parse a key-value pair */
int parseJson(char *pcJsonStr)
{
cJSON *pstJRoot = NULL;
cJSON *pstJFileID = NULL;
cJSON *pstFileName = NULL;
cJSON *pstJPort = NULL;
cJSON *pstSubFile = NULL;
cJSON *pstSize = NULL;
cJSON *pstAuthor = NULL;
cJSON *pstSubFileArray = NULL;
cJSON *pstAuthorArray = NULL;
cJSON *pstAuthorRoot = NULL;
cJSON *pstAuthorName = NULL;
cJSON *pstSubFileRoot = NULL;
cJSON *pstSubFileName = NULL;
int iSubFileSize = 0;
int iSubFileIndex = 0;
int iAuthorSize = 0;
int iAuthorIndex = 0;
pstJRoot = cJSON_Parse(pcJsonStr);
if (!pstJRoot)
{
printf("Error before: [%s]\n",cJSON_GetErrorPtr());
return -1;
}
printf("---- Start parse ----\n");
/* fileName */
pstFileName=cJSON_GetObjectItem(pstJRoot,"fileName");
if (cJSON_IsString(pstFileName) && (pstFileName->valuestring != NULL))
{
printf("%s=%s\n", pstFileName->string,pstFileName->valuestring);
}
/* fileid */
pstJFileID=cJSON_GetObjectItem(pstJRoot,"fileid");
if (cJSON_IsString(pstJFileID) && (pstJFileID->valuestring != NULL))
{
printf("%s=%s\n", pstJFileID->string,pstJFileID->valuestring);
}
/* port */
pstJPort=cJSON_GetObjectItem(pstJRoot,"port");
if (cJSON_IsNumber(pstJPort))
{
printf("%s=%d\n", pstJPort->string,pstJPort->valueint);
}
/* 方法一: cJSON_GetArraySize for */
///* subfile array */
//pstSubFileArray = cJSON_GetObjectItem(pstJRoot, "subfile");
//iSubFileSize = cJSON_GetArraySize(pstSubFileArray);
//
//for(iSubFileIndex = 0; iSubFileIndex < iSubFileSize; iSubFileIndex++)
//{
// pstSubFileRoot = cJSON_GetArrayItem(pstSubFileArray, iSubFileIndex);
// if(NULL == pstSubFileRoot)
// {
// break;
// }
//
// /* subfileName */
// pstSubFileName = cJSON_GetObjectItemCaseSensitive(pstSubFileRoot, "subfileName");
// if (cJSON_IsString(pstSubFileName) && (pstSubFileName->valuestring != NULL))
// {
// printf(" %s=%s\n", pstSubFileName->string,pstSubFileName->valuestring);
// }
//
// /* size */
// pstSize = cJSON_GetObjectItemCaseSensitive(pstSubFileRoot, "size");
// if (cJSON_IsNumber(pstSize))
// {
// printf(" %s=%f\n", pstSize->string,pstSize->valuedouble);
// }
//
// /* author array */
// pstAuthorArray = cJSON_GetObjectItem(pstSubFileRoot, "author");
// iAuthorSize = cJSON_GetArraySize(pstAuthorArray);
//
// for(iAuthorIndex = 0; iAuthorIndex < iAuthorSize; iAuthorIndex++)
// {
// pstAuthorRoot = cJSON_GetArrayItem(pstAuthorArray, iAuthorIndex);
// if(NULL == pstAuthorRoot)
// {
// break;
// }
//
// /* author name */
// pstAuthorName = cJSON_GetObjectItemCaseSensitive(pstAuthorRoot, "name");
// if (cJSON_IsString(pstAuthorName) && (pstAuthorName->valuestring != NULL))
// {
// printf(" %s=%s\n", pstAuthorName->string,pstAuthorName->valuestring);
// }
// }
//
//}
/* 方法二: cJSON_ArrayForEach */
/* subfile object */
pstSubFile = cJSON_GetObjectItem(pstJRoot, "subfile");
cJSON_ArrayForEach(pstSubFileRoot, pstSubFile)
{
/* subfileName */
pstSubFileName = cJSON_GetObjectItemCaseSensitive(pstSubFileRoot, "subfileName");
if (cJSON_IsString(pstSubFileName) && (pstSubFileName->valuestring != NULL))
{
printf(" %s=%s\n", pstSubFileName->string,pstSubFileName->valuestring);
}
/* size */
pstSize = cJSON_GetObjectItemCaseSensitive(pstSubFileRoot, "size");
if (cJSON_IsNumber(pstSize))
{
printf(" %s=%f\n", pstSize->string,pstSize->valuedouble);
}
/* author object */
pstAuthor = cJSON_GetObjectItem(pstSubFileRoot, "author");
cJSON_ArrayForEach(pstAuthorRoot, pstAuthor)
{
/* author name */
pstAuthorName = cJSON_GetObjectItemCaseSensitive(pstAuthorRoot, "name");
if (cJSON_IsString(pstAuthorName) && (pstAuthorName->valuestring != NULL))
{
printf(" %s=%s\n", pstAuthorName->string,pstAuthorName->valuestring);
}
}
}
cJSON_Delete(pstJRoot);
return 0;
}
/* read file */
void readFile(char *pcFileName)
{
FILE *pFile;
int iFileLen;
char *pcData;
pFile=fopen(pcFileName,"rb");
fseek(pFile,0,SEEK_END);
iFileLen=ftell(pFile);
fseek(pFile,0,SEEK_SET);
pcData=(char*)malloc(iFileLen+1);
fread(pcData,1,iFileLen,pFile);
fclose(pFile);
printf("read file %s complete, iFileLen=%d.\n",pcFileName,iFileLen);
int ret = parseJson(pcData);
if(-1 == ret)
{
printf("parseJson failed.\n");
}
free(pcData);
}
int main(int argc, char **argv)
{
readFile("test.txt");
return 0;
}
4.3 编译及测试结果
4.3.1 文件目录
-rw-r--r-- 1 root root 77833 Nov 16 2020 cJSON.c
-rw-r--r-- 1 root root 15828 Nov 16 2020 cJSON.h
-rw-r--r-- 1 root root 3007 Jan 1 11:21 jsonTest.c
-rw-r--r-- 1 root root 734 Jan 1 11:06 test.txt
4.3.2 编译命令
编译命令,编译json 静态库
gcc -c cJSON.c
ar -cr libjson.a cJSON.o
gcc -o res jsonTest.c -L. -ljson
编译后:
-rw-r--r-- 1 root root 77833 Nov 16 2020 cJSON.c
-rw-r--r-- 1 root root 15828 Nov 16 2020 cJSON.h
-rw-r--r-- 1 root root 43616 Jan 2 00:36 cJSON.o
-rw-r--r-- 1 root root 3007 Jan 1 11:21 jsonTest.c
-rw-r--r-- 1 root root 45706 Jan 2 00:38 libjson.a
-rwxr-xr-x 1 root root 43392 Jan 2 00:39 res
-rw-r--r-- 1 root root 734 Jan 1 11:06 test.txt
4.3.3 结果
[root@glusterfs json]# ./res
read file test.txt complete, iFileLen=734.
---- Start parse ----
filename=test.tar
fileid=888
port=1024
subfileName=aaa.txt
size=102.000000
name=hani
name=lufei
subfileName=bbb.docx
size=560.000000
name=suolong
name=namei
5. 代码示例-封装json
5.1 代码示例 createJson.c
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include "cJSON.h"
/* createJson : create json */
int createJson()
{
cJSON *pstJRoot = NULL;
cJSON *pstSubFile1 = NULL;
cJSON *pstSubFile2 = NULL;
cJSON *pstAuthor1_name1 = NULL;
cJSON *pstAuthor1_name2 = NULL;
cJSON *pstAuthor2_name1 = NULL;
cJSON *pstAuthor2_name2 = NULL;
cJSON *pstSubFileArray1 = NULL;
cJSON *pstSubFileArray2 = NULL;
cJSON *pstAuthorArray1 = NULL;
cJSON *pstAuthorArray2 = NULL;
char *pcRes = NULL;
printf("---- Start create json ----\n");
pstJRoot = cJSON_CreateObject();
if(pstJRoot == NULL)
{
printf("[error]: create pstJRoot failed. \n");
return -1;
}
cJSON_AddStringToObject(pstJRoot, "filename", "test.tar");
cJSON_AddStringToObject(pstJRoot, "fileid", "888");
cJSON_AddNumberToObject(pstJRoot, "port", 1024);
/* create pstSubFileArray1 */
pstSubFileArray1 = cJSON_AddArrayToObject(pstJRoot, "subfile");
pstSubFile1 = cJSON_CreateObject();
if(pstSubFile1 == NULL)
{
printf("[error]: create pstSubFile1 failed. \n");
return -1;
}
cJSON_AddItemToArray(pstSubFileArray1, pstSubFile1);
cJSON_AddStringToObject(pstSubFile1, "subfileName", "aaa.txt");
cJSON_AddNumberToObject(pstSubFile1, "size", 102);
/* create pstAuthorArray1 */
pstAuthorArray1 = cJSON_AddArrayToObject(pstSubFile1, "author");
pstAuthor1_name1 = cJSON_CreateObject();
if(pstAuthor1_name1 == NULL)
{
printf("[error]: create pstAuthor1_name1 failed. \n");
return -1;
}
cJSON_AddItemToArray(pstAuthorArray1, pstAuthor1_name1);
cJSON_AddStringToObject(pstAuthor1_name1, "name", "hani");
pstAuthor1_name2 = cJSON_CreateObject();
if(pstAuthor1_name2 == NULL)
{
printf("[error]: create pstAuthor1_name2 failed. \n");
return -1;
}
cJSON_AddItemToArray(pstAuthorArray1, pstAuthor1_name2);
cJSON_AddStringToObject(pstAuthor1_name2, "name", "lufei");
/* create pstSubFileArray2 */
pstSubFileArray2 = cJSON_AddArrayToObject(pstJRoot, "subfile");
pstSubFile2 = cJSON_CreateObject();
if(pstSubFile2 == NULL)
{
printf("[error]: create pstSubFile2 failed. \n");
return -1;
}
cJSON_AddItemToArray(pstSubFileArray2, pstSubFile2);
cJSON_AddStringToObject(pstSubFile2, "subfileName", "bbb.docx");
cJSON_AddNumberToObject(pstSubFile2, "size", 102);
/* create pstAuthorArray2 */
pstAuthorArray2 = cJSON_AddArrayToObject(pstSubFile2, "author");
pstAuthor2_name1 = cJSON_CreateObject();
if(pstAuthor2_name1 == NULL)
{
printf("[error]: create pstAuthor2_name1 failed. \n");
return -1;
}
cJSON_AddItemToArray(pstAuthorArray2, pstAuthor2_name1);
cJSON_AddStringToObject(pstAuthor2_name1, "name", "suolong");
pstAuthor2_name2 = cJSON_CreateObject();
if(pstAuthor2_name2 == NULL)
{
printf("[error]: create pstAuthor2_name2 failed. \n");
return -1;
}
cJSON_AddItemToArray(pstAuthorArray2, pstAuthor2_name2);
cJSON_AddStringToObject(pstAuthor2_name2, "name", "namei");
pcRes = cJSON_Print(pstJRoot);
if (pcRes == NULL)
{
printf("[error]: cJSON_Print failed. \n");
}
printf("result:\n%s\n",pcRes);
cJSON_Delete(pstJRoot);
free(pcRes);
return 0;
}
int main(int argc, char **argv)
{
int res = createJson();
if(-1 == res)
{
printf("createJson failed \n");
}
return 0;
}
5.2 结果
[root@glusterfs json]# gcc -o res createJson.c -L. -ljson
[root@glusterfs json]# ./res
---- Start create json ----
result:
{
"filename": "test.tar",
"fileid": "888",
"port": 1024,
"subfile": [{
"subfileName": "aaa.txt",
"size": 102,
"author": [{
"name": "hani"
}, {
"name": "lufei"
}]
}],
"subfile": [{
"subfileName": "bbb.docx",
"size": 102,
"author": [{
"name": "suolong"
}, {
"name": "namei"
}]
}]
}
6. 重点!!!pcJsonStr (pcRes)需要释放
上面的 createJson.c 中: free(pcRes);
pcRes = cJSON_Print(pstJRoot);
if (pcRes == NULL)
{
printf("[error]: cJSON_Print failed. \n");
}
printf("result:\n%s\n",pcRes);
cJSON_Delete(pstJRoot);
free(pcRes);.
因为: cJSON_Print 内调用了 cJSON_malloc
参考:
https://cloud.tencent.com/developer/article/1055318
https://github.com/DaveGamble/cJSON
https://www.json.org/json-en.html