- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
思考这么一个问题:对于不同的设备如何进行数据交换?可以考虑使用轻量级别的 JSON 格式.
那么需要我们手写一个 JSON 解析器吗?这大可不必,因为已经有前辈提供了开源的轻量级的 JSON 解析器——cJSON。我们会用就可以了,当然你也可以深入源码进行学习.
下图则向我们展示了如何通过 cJSON 实现 Client 与 Server 的数据交换:
在介绍 cJSON 之前,先来对 JSON 这个数据格式有个简单了解.
JSON 指的是 JavaScript 对象表示法(JavaScript Object Notation)。但它并不是编程语言,而是一种可以在服务器和客户端之间传输的 数据交换格式 .
JSON 的两种数据结构:
从上述描述中,我们可以获得如下四种信息:
JSON 对象具体格式如下图所示:
{
开始,以 }
结束,是若干「key / value 对」的集合 :
分隔 ,
分隔 注意事项:
如,这是一个合法的 JSON 对象:
{
"name" : "张三"
}
这也是一个合法的 JSON 对象:
{
"name" : "张三",
"age" : 18,
"sex" : "男"
}
JSON 数组具体格式如下图所示:
[
开始, ]
结束,是若干 value 的有序集合 ,
分隔 如,这是一个合法的 JSON 数组:
[
"张三",
18,
"男"
]
该数组包含三个 value,分别为 string、number、string 。
这也是一个合法的 JSON 数组:
[
{
"name" : "张三",
"age" : 18,
"sex" : "男"
},
{
"name" : "李四",
"age" : 19,
"sex" : "男"
}
]
该数组包含两个 Object,每个 Object 又包含若干 key / value.
值(value)可以是:
value 可以是简单的用双引号引起来的 string 串,也可以是一个数值;或布尔值(true or false),或 null.
当然也可以是复杂的 object 或 array,这些取值是可以嵌套的.
在「1.4 JSON 数组」中,第二个例子就是一个嵌套的举例,当然也可以这么嵌套:
{
"class_name" : "计科一班",
"student_num" : 2,
"student_info" :
[
{
"name" : "张三",
"age" : 18,
"sex" : "男"
},
{
"name" : "李四",
"age" : 19,
"sex" : "男"
}
]
}
源码下载: https://www.aliyundrive.com/s/GgcJZvHBdPq 。
编译环境:CentOS 7 。
源码中包含 cJSON.h 和 cJSON.c,以及一个测试程序 main.c,测试程序的功能是输出一个 JSON 字符串:
{
"name": "张三",
"age": 18,
"sex": "男"
}
你可以通过下面两种方法运行该程序:
$ gcc -g -Wall *.c -l m
,会默认生成一个 a.out 文件,执行该文件即可 由于源码中使用了 pow 函数,所以一定要链接上 math 库,也就是 -l m 指令.
如果在编译过程中报好多 warning(如下图所示)警告:
不要慌,这并不影响程序的运行,如果你想消除这些警告,不妨将 cJSON.c 格式化一下(用 VSCode 按下alt+shift+F).
至于原理,不妨参考这篇文章: gcc编译警告关于(warning: XXX...[-Wmisleading-indentation] if(err)之类的问题) 。
首先,我们先对 cJSON 的结构体有个初步了解,其定义如下:
typedef struct cJSON
{
struct cJSON *next, *prev;
struct cJSON *child;
int type;
char *valuestring;
int valueint;
double valuedouble;
char *string;
} cJSON;
由于我在实际使用过程中未涉及 bool、null,所以举例中暂不涉及这两种类型.
在正式讲解之前,让我们先看一下与反序列化相关的函数:
函数 | 解释说明 | 返回值 |
---|---|---|
cJSON_Parse | 将 JSON 字符串 反序列化 为 cJSON 结构体 | cJSON * |
cJSON_GetObjectItem | 获取 JSON 对象中的指定项 | cJSON * |
cJSON_GetArrayItem | 获取 JSON 数组中的第 i 个 JSON 项 | cJSON * |
cJSON_GetArraySize | 获取 JSON 数组的大小(该数组中包含的 JSON 项个数) | int |
cJSON_Delete | 删除 cJSON 结构体 | void |
对于一个 JSON 字符串:
{
"name": "张三",
"age": 18,
"sex": "男"
}
我们可以在代码中通过调用 cJSON_Parse(const char *value) 函数将 JSON 字符串 value 反序列化为 cJSON 结构体:
cJSON *root = cJSON_Parse(pcJson); // pcJson 为从文件中获取的 JSON 字符串
if (NULL == root)
{
printf("fail to call cJSON_Parse\n");
exit(0);
}
反序列化后的 JSON 字符串,大概长这个样子:
那么我们如何获取 name、age、sex 对应的值呢?可以通过调用 cJSON *cJSON_GetObjectItem() 函数从 object 中获取.
cJSON *pName = cJSON_GetObjectItem(root, "name");
printf("name [%s]\n", pName->valuestring);
cJSON *pAge = cJSON_GetObjectItem(root, "age");
printf("age [%d]\n", pAge->valueint);
cJSON *pSex = cJSON_GetObjectItem(root, "sex");
printf("sex [%s]\n", pSex->valuestring);
cJSON *cJSON_GetObjectItem(cJSON *object, const char *string)
:从 object 的所有 child 中检索键为 string 的 JSON 项
完整代码如下:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>
#include "cJSON.h"
#define STRING_LEN_MAX 2048
void GetJSONFromFile(const char *FILENAME, char **ppcJson)
{
FILE *fp = fopen(FILENAME, "r");
if (NULL == fp)
{
printf("file open error\n");
exit(0);
}
char *pcJson = (char *)malloc(STRING_LEN_MAX);
memset(pcJson, 0, STRING_LEN_MAX);
do
{
fgets(pcJson + strlen(pcJson), STRING_LEN_MAX - strlen(pcJson), fp);
} while (!feof(fp));
*ppcJson = pcJson;
fclose(fp);
}
int main()
{
char *pcJson;
GetJSONFromFile("test.json", &pcJson); // 从文件 test.json 中获取 JSON 字符串
cJSON *root = cJSON_Parse(pcJson);
if (NULL == root)
{
printf("fail to call cJSON_Parse\n");
exit(0);
}
cJSON *pName = cJSON_GetObjectItem(root, "name");
printf("name [%s]\n", pName->valuestring);
cJSON *pAge = cJSON_GetObjectItem(root, "age");
printf("age [%d]\n", pAge->valueint);
cJSON *pSex = cJSON_GetObjectItem(root, "sex");
printf("sex [%s]\n", pSex->valuestring);
cJSON_Delete(root); // 手动调用 cJSON_Delete 进行内存回收
return 0;
}
对于一个复杂些的 JSON 字符串:
{
"class_name": "计科一班",
"stu_num" : 2,
"stu_info" :
[
{
"name": "张三",
"age": 18,
"sex": "男"
},
{
"name": "李四",
"age": 20,
"sex": "男"
}
]
}
反序列化该字符串依旧很简单,只需我们在代码中调用 cJSON_Parse() 即可,而难点在于如何解析.
先来看一下该字符串反序列化后长啥样:
对于 class_name 以及 stu_name,我们可以很容易就解析出来:
cJSON *pClassName = cJSON_GetObjectItem(root, "class_name");
printf("class name [%s]\n", pClassName->valuestring);
cJSON *pStuNum = cJSON_GetObjectItem(root, "stu_num");
printf("stu num [%d]\n", pStuNum->valueint);
那么如何获取更深层次的 name、age 以及 sex 呢?
通过 JSON 字符串可以知道,stu_info 是一个 JSON 数组,那么我们首先要做的是将这个数组从 root 中摘出来:
cJSON *pArray = cJSON_GetObjectItem(root, "stu_info");
if (NULL == pArray)
{
printf("not find stu_info\n");
goto err;
}
接着将该数组中的各个项依次取出.
cJSON *item_0 = cJSON_GetArrayItem(pArray, 0);
cJSON *item_1 = cJSON_GetArrayItem(pArray, 1);
cJSON_GetArrayItem(cJSON *array, int item) :从 JSON 数组 array 中获取第 item 项(下标从 0 开始) 。
如果存在,则返回相应的 JSON 项 。
反之返回 NULL.
最后,将 name、age、sex 分别从 item_0 / item_1 中取出即可.
上述操作只是为了讲解如何获取更深层次的 JSON 项,实际操作中会这么写:
int iArraySize = cJSON_GetArraySize(pArray);
for (i = 0; i < iArraySize; i++)
{
printf("******** Stu[%d] info ********\n", i + 1);
cJSON *item = cJSON_GetArrayItem(pArray, i);
cJSON *pName = cJSON_GetObjectItem(item, "name");
printf("name [%s]\n", pName->valuestring);
cJSON *pAge = cJSON_GetObjectItem(item, "age");
printf("age [%d]\n", pAge->valueint);
cJSON *pSex = cJSON_GetObjectItem(item, "sex");
printf("sex [%s]\n", pSex->valuestring);
}
就跟剥洋葱似的,先将外层的 stu_info 剥出来,然后在剥出内层的 item,最后将 name、age、sex 从 item 中分离出来.
对于更多层次的 JSON 处理,也是同样的操作,你只需要保证在解析你所需的 JSON 项前,其父节点已被解析.
完整代码如下:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>
#include "cJSON.h"
#define STRING_LEN_MAX 2048
void GetJSONFromFile(const char *FILENAME, char **ppcJson)
{
FILE *fp = fopen(FILENAME, "r");
if (NULL == fp)
{
printf("file open error\n");
exit(0);
}
char *pcJson = (char *)malloc(STRING_LEN_MAX);
memset(pcJson, 0, STRING_LEN_MAX);
do
{
fgets(pcJson + strlen(pcJson), STRING_LEN_MAX - strlen(pcJson), fp);
} while (!feof(fp));
*ppcJson = pcJson;
fclose(fp);
}
int main()
{
char *pcJson;
GetJSONFromFile("test.json", &pcJson);
cJSON *root = cJSON_Parse(pcJson);
if (NULL == root)
{
printf("fail to call cJSON_Parse\n");
exit(0);
}
cJSON *pClassName = cJSON_GetObjectItem(root, "class_name");
printf("class name [%s]\n", pClassName->valuestring);
cJSON *pStuNum = cJSON_GetObjectItem(root, "stu_num");
printf("stu num [%d]\n", pStuNum->valueint);
cJSON *pArray = cJSON_GetObjectItem(root, "stu_info");
if (NULL == pArray)
{
printf("not find stu_info\n");
goto err;
}
int i;
int iArraySize = cJSON_GetArraySize(pArray);
for (i = 0; i < iArraySize; i++)
{
printf("******** Stu[%d] info ********\n", i + 1);
cJSON *item = cJSON_GetArrayItem(pArray, i);
cJSON *pName = cJSON_GetObjectItem(item, "name");
printf("name [%s]\n", pName->valuestring);
cJSON *pAge = cJSON_GetObjectItem(item, "age");
printf("age [%d]\n", pAge->valueint);
cJSON *pSex = cJSON_GetObjectItem(item, "sex");
printf("sex [%s]\n", pSex->valuestring);
}
err:
cJSON_Delete(root); // 手动调用 cJSON_Delete 进行内存回收
return 0;
}
前面我们一直在介绍如何将一个 JSON 字符串反序列化为 cJSON 结构体,下面我们来介绍一下如何将 cJSON 结构体序列化为 JSON 字符串.
首先,我们要先有一个 cJSON 结构体,构造 cJSON 结构体的相关函数如下:
函数 | 解释说明 | 返回值 |
---|---|---|
cJSON_CreateObject | 创建一个 object 类型的 JSON 项 | cJSON * |
cJSON_CreateArray | 创建一个 array 类型的 JSON 项 | cJSON * |
cJSON_CreateString | 创建一个值为 string 类型的 JSON 项 | cJSON * |
cJSON_CreateNumber | 创建一个值为 number 类型的 JSON 项 | cJSON * |
cJSON_AddItemToObject | 将 JSON 项添加到 object 中 | void |
cJSON_AddItemToArray | 将 JSON 项添加到 array 中 | void |
cJSON_AddNumberToObject | 创建一个值为 number 类型的 JSON 项并添加到 JSON 对象中 | void |
cJSON_AddStringToObject | 创建一个值为 string 类型的 JSON 项并添加到 JSON 对象中 | void |
cJSON_Print | 将 cJSON 结构体 序列化 为 JSON 字符串(有格式) | char * |
cJSON_PrintUnformatted | 将 cJSON 结构体序列化为 JSON 字符串(无格式) | char * |
cJSON_Delete | 删除 cJSON 结构体 | void |
假设我们想要获取的 JSON 字符串为:
{
"name": "张三",
"age": 18,
"sex": "男"
}
我们该如何构造 cJSON 结构体呢?
还记得这个 JSON 字符串反序列化的样子吗?不记得也没关系,因为我马上就要张贴了😝 。
根据图示可知,我们要先有一个根节点 root.
由于本次样例中的 JSON 字符串是一个 JSON 对象,所以我们需要通过 cJSON_CreateObject() 函数来创建一个 object 类型的 root:
cJSON *root = cJSON_CreateObject();
接下来我们需要将 name、age、sex 分别加入到 root 中:
cJSON_AddStringToObject()
将字符串类型的 name、sex 加入到 root 中 cJSON_AddNumberToObject()
将数值类型的 age 加入到 root 中 具体操作如下:
cJSON_AddStringToObject(root, "name", "张三");
cJSON_AddNumberToObject(root, "age", 18);
cJSON_AddStringToObject(root, "sex", "男");
cJSON_AddStringToObject(object,name,s)
:将键值对(name / s)加入到 object 中 cJSON_AddNumberToObject(object,name,n)
:将键值对(name / n)加入到 object 中 经过上述操作,我们就可以得到如图 6 所示的 cJSON 结构体。那如何获取基于该结构体的 JSON 字符串呢?
很简单,调用函数 cJSON_Print() 或 cJSON_PrintUnformatted() 即可实现:
char *pJsonFormatted = cJSON_Print(root);
puts(pJsonFormatted);
char *pJsonUnformatted = cJSON_PrintUnformatted(root);
puts(pJsonUnformatted);
cJSON_Print() 和 cJSON_PrintUnformatted() ,这两个 API 的区别在于:一个是没有格式的,也就是转换出的字符串中间不会有换行、对齐之类的格式存在。而 cJSON_Print 打印出来是我们看起来很舒服的格式,仅此而已.
完整代码如下:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>
#include "cJSON.h"
int main()
{
cJSON *root = cJSON_CreateObject();
cJSON_AddStringToObject(root, "name", "张三");
cJSON_AddNumberToObject(root, "age", 18);
cJSON_AddStringToObject(root, "sex", "男");
char *pJsonFormatted = cJSON_Print(root);
puts(pJsonFormatted);
char *pJsonUnformatted = cJSON_PrintUnformatted(root);
puts(pJsonUnformatted);
cJSON_Delete(root); // 手动调用 cJSON_Delete 进行内存回收
// 记得回收 pJsonFormatted 和 pJsonUnformatted
if (NULL != pJsonFormatted)
{
free(pJsonFormatted);
}
if (NULL != pJsonUnformatted)
{
free(pJsonUnformatted);
}
return 0;
}
这次我们要获取的 JSON 字符串为:
{
"class_name": "计科一班",
"stu_num" : 2,
"stu_info" :
[
{
"name": "张三",
"age": 18,
"sex": "男"
},
{
"name": "李四",
"age": 20,
"sex": "男"
}
]
}
对应的反序列化后的模样如下图所示:
具体做法为:
大功告成,具体代码如下:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <ctype.h>
#include "cJSON.h"
int main()
{
// 步骤一
cJSON *root = cJSON_CreateObject();
// 步骤二
cJSON_AddStringToObject(root, "class_name", "计科一班");
cJSON_AddNumberToObject(root, "stu_num", 2);
cJSON *pArray = cJSON_CreateArray();
cJSON_AddItemToObject(root, "stu_info", pArray);
// 步骤三
cJSON *pObject_1 = cJSON_CreateObject();
cJSON_AddStringToObject(pObject_1, "name", "张三");
cJSON_AddNumberToObject(pObject_1, "age", 18);
cJSON_AddStringToObject(pObject_1, "sex", "男");
cJSON *pObject_2 = cJSON_CreateObject();
cJSON_AddStringToObject(pObject_2, "name", "李四");
cJSON_AddNumberToObject(pObject_2, "age", 19);
cJSON_AddStringToObject(pObject_2, "sex", "男");
// 步骤四
cJSON_AddItemToArray(pArray, pObject_1);
cJSON_AddItemToArray(pArray, pObject_2);
char *pJson = cJSON_Print(root);
puts(pJson);
cJSON_Delete(root); // 手动调用 cJSON_Delete 进行内存回收
if (NULL != pJson) // 回收 pJson
{
free(pJson);
}
return 0;
}
最后此篇关于cJson 学习笔记的文章就讲到这里了,如果你想了解更多关于cJson 学习笔记的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
关闭。这个问题是opinion-based .它目前不接受答案。 想要改进这个问题? 更新问题,以便 editing this post 可以用事实和引用来回答它. 关闭 9 年前。 Improve
介绍篇 什么是MiniApis? MiniApis的特点和优势 MiniApis的应用场景 环境搭建 系统要求 安装MiniApis 配置开发环境 基础概念 MiniApis架构概述
我正在从“JavaScript 圣经”一书中学习 javascript,但我遇到了一些困难。我试图理解这段代码: function checkIt(evt) { evt = (evt) ? e
package com.fastone.www.javademo.stringintern; /** * * String.intern()是一个Native方法, * 它的作用是:如果字
您会推荐哪些资源来学习 AppleScript。我使用具有 Objective-C 背景的传统 C/C++。 我也在寻找有关如何更好地开发和从脚本编辑器获取更快文档的技巧。示例提示是“查找要编写脚本的
关闭。这个问题不满足Stack Overflow guidelines .它目前不接受答案。 想改善这个问题吗?更新问题,使其成为 on-topic对于堆栈溢出。 4年前关闭。 Improve thi
关闭。这个问题不满足Stack Overflow guidelines .它目前不接受答案。 想改善这个问题吗?更新问题,使其成为 on-topic对于堆栈溢出。 7年前关闭。 Improve thi
关闭。这个问题不符合 Stack Overflow guidelines 。它目前不接受答案。 想改善这个问题吗?更新问题,以便堆栈溢出为 on-topic。 6年前关闭。 Improve this
我是塞内加尔的阿里。我今年60岁(也许这是我真正的问题-笑脸!!!)。 我正在学习Flutter和Dart。今天,我想使用给定数据模型的列表(它的名称是Mortalite,请参见下面的代码)。 我尝试
关闭。这个问题是off-topic .它目前不接受答案。 想改进这个问题? Update the question所以它是on-topic对于堆栈溢出。 9年前关闭。 Improve this que
学习 Cappuccino 的最佳来源是什么?我从事“传统”网络开发,但我对这个新框架非常感兴趣。请注意,我对 Objective-C 毫无了解。 最佳答案 如上所述,该网站是一个好地方,但还有一些其
我正在学习如何使用 hashMap,有人可以检查我编写的这段代码并告诉我它是否正确吗?这个想法是有一个在公司工作的员工列表,我想从 hashMap 添加和删除员工。 public class Staf
我正在尝试将 jQuery 与 CoffeScript 一起使用。我按照博客中的说明操作,指示使用 $ -> 或 jQuery -> 而不是 .ready() 。我玩了一下代码,但我似乎无法理解我出错
还在学习,还有很多问题,所以这里有一些。我正在进行 javascript -> PHP 转换,并希望确保这些做法是正确的。是$dailyparams->$calories = $calories;一条
我目前正在学习 SQL,以便从我们的 Magento 数据库制作一个简单的 RFM 报告,我目前可以通过导出两个查询并将它们粘贴到 Excel 模板中来完成此操作,我想摆脱 Excel 模板。 我认为
我知道我很可能会因为这个问题而受到抨击,但没有人问,我求助于你。这是否是一个正确的 javascript > php 转换 - 在我开始不良做法之前,我想知道这是否是解决此问题的正确方法。 JavaS
除了 Ruby-Doc 之外,哪些来源最适合获取一些示例和教程,尤其是关于 Ruby 中的 Tk/Tile?我发现自己更正常了 http://www.tutorialspoint.com/ruby/r
我只在第一次收到警告。这正常吗? >>> cv=LassoCV(cv=10).fit(x,y) C:\Python27\lib\site-packages\scikit_learn-0.14.1-py
按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the
As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be
我是一名优秀的程序员,十分优秀!