cjson使用_LiteOS云端对接教程01-cJSON组件使用教程
1. JSON与cJSON
JSON —— 轻量级的数据格式
JSON 全称 JavaScript Object Notation,即 JS对象简谱,是一种轻量级的数据格式。
它采用完全独立于编程语言的文本格式来存储和表示数据,语法简洁、层次结构清晰,易于人阅读和编写,同时也易于机器解析和生成,有效的提升了网络传输效率。
JSON语法规则
JSON对象是一个无序的"名称/值"键值对的集合:
- 以"
{
"开始,以"}
"结束,允许嵌套使用;
- 每个名称和值成对出现,名称和值之间使用"
:
"分隔;
- 键值对之间用"
,
"分隔
- 在这些字符前后允许存在无意义的空白符;
对于键值,可以有如下值:
- 一个新的json对象
- 数组:使用"
[
"和"]
"表示
- 数字:直接表示,可以是整数,也可以是浮点数
- 字符串:使用引号
"
表示
- 字面值:false、null、true中的一个(必须是小写)
示例如下:
{"name": "mculover666","age": 22,"weight": 55.5"address":{"country": "China","zip-code": 111111},"skill": ["c", "Java", "Python"],"student": false
}
LiteOS中的cJSON组件
cJSON是一个使用C语言编写的JSON数据解析器,具有超轻便,可移植,单文件的特点,使用MIT开源协议。
LiteOS中已经移植了cJSON,作为一个组件使用,源码在sdkIoT_LINK_1.0.0iot_linkcJSON
中,其源码文件只有两个:
cJSON.h
cJSON.c
使用的时候,只需要将这两个文件复制到工程目录,然后包含头文件cJSON.h
即可,如下:
#include "cJSON.h"
2. cJSON数据结构和设计思想
cJSON的设计思想从其数据结构上就能反映出来。
cJSON使用cJSON结构体来表示一个JSON数据,定义在cJSON.h
中,源码如下:
/* The cJSON structure: */
typedef struct cJSON
{/* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */struct cJSON *next;struct cJSON *prev;/* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */struct cJSON *child;/* The type of the item, as above. */int type;/* The item's string, if type==cJSON_String and type == cJSON_Raw */char *valuestring;/* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */int valueint;/* The item's number, if type==cJSON_Number */double valuedouble;/* The item's name string, if this item is the child of, or is in the list of subitems of an object. */char *string;
} cJSON;
cJSON的设计很巧妙。
首先,它不是将一整段JSON数据抽象出来,而是将其中的一条JSON数据抽象出来,也就是一个键值对,用上面的结构体 strcut cJSON
来表示,其中用来存放值的成员列表如下:
String
:用于表示该键值对的名称;
type
:用于表示该键值对中值的类型;
valuestring
:如果键值类型(type)是字符串,则将该指针指向键值;
valueint
:如果键值类型(type)是整数,则将该指针指向键值;
valuedouble
:如果键值类型(type)是浮点数,则将该指针指向键值;
其次,一段完整的JSON数据中由很多键值对组成,并且涉及到键值对的查找、删除、添加,所以使用链表来存储整段JSON数据,如上面的代码所示:
next
指针:指向下一个键值对
prev
指针指向上一个键值对
最后,因为JSON数据支持嵌套,所以一个键值对的值会是一个新的JSON数据对象(一条新的链表),也有可能是一个数组,方便起见,在cJSON中,数组也表示为一个数组对象,用链表存储,所以:
在键值对结构体中,当该键值对的值是一个嵌套的JSON数据或者一个数组时,由child
指针指向该条新链表。
3. 开启cJSON组件
在LiteOS中,cJSON组件默认是未开启的,使用宏定义CONFIG_JSON_ENABLE
开启。
开启之后,LiteOS会自动进行初始化,并且使用cJSON的内存钩子将cJSON申请内存的方式变为使用osalmalloc申请,自动初始化代码在`linkmain.c`文件中:
4. JSON数据封装
封装方法
封装JSON数据的过程,其实就是创建链表和向链表中添加节点的过程。
首先来讲述一下链表中的一些术语:
- 头指针:指向链表头结点的指针;
- 头结点:不存放有效数据,方便链表操作;
- 首节点:第一个存放有效数据的节点;
- 尾节点:最后一个存放有效数据的节点;
明白了这几个概念之后,我们开始讲述创建一段完整的JSON数据,即如何创建一条完整的链表。
- ① 创建头指针:
cJSON* cjson_test = NULL;
- ② 创建头结点,并将头指针指向头结点:
cjson_test = cJSON_CreateObject();
- ③ 尽情的向链表中添加节点:
cJSON_AddNullToObject(cJSON * const object, const char * const name);cJSON_AddTrueToObject(cJSON * const object, const char * const name);cJSON_AddFalseToObject(cJSON * const object, const char * const name);cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean);cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number);cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string);cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw);cJSON_AddObjectToObject(cJSON * const object, const char * const name);cJSON_AddArrayToObject(cJSON * const object, const char * const name);
输出JSON数据
上面讲述,一段完整的JSON数据就是一条长长的链表,那么,如何打印出这段JSON数据呢?
cJSON提供了一个API,可以将整条链表中存放的JSON信息输出到一个字符串中:
(char *) cJSON_Print(const cJSON *item);
使用的时候,只需要接收该函数返回的指针地址即可。
封装数据和打印数据示例
单纯的讲述方法还不够,下面用一个例子来说明,封装出开头给出的那段JSON数据。
首先基于HelloWorld工程,创建一个存放示例文件的文件夹cloud_test_demo
,并新建一个实验文件cjson_print_demo.c
,编写如下代码:
#include <osal.h>
#include <stdio.h>
#include <cJSON.h>static int cjson_print_demo_entry()
{cJSON* cjson_test = NULL;cJSON* cjson_address = NULL;cJSON* cjson_skill = NULL;char* str = NULL;/* 创建一个JSON数据对象(链表头结点) */cjson_test = cJSON_CreateObject();/* 添加一条字符串类型的JSON数据(添加一个链表节点) */cJSON_AddStringToObject(cjson_test, "name", "mculover666");/* 添加一条整数类型的JSON数据(添加一个链表节点) */cJSON_AddNumberToObject(cjson_test, "age", 22);/* 添加一条浮点类型的JSON数据(添加一个链表节点) */cJSON_AddNumberToObject(cjson_test, "weight", 55.5);/* 添加一个嵌套的JSON数据(添加一个链表节点) */cjson_address = cJSON_CreateObject();cJSON_AddStringToObject(cjson_address, "country", "China");cJSON_AddNumberToObject(cjson_address, "zip-code", 111111);cJSON_AddItemToObject(cjson_test, "address", cjson_address);/* 添加一个数组类型的JSON数据(添加一个链表节点) */cjson_skill = cJSON_CreateArray();cJSON_AddItemToArray(cjson_skill, cJSON_CreateString( "C" ));cJSON_AddItemToArray(cjson_skill, cJSON_CreateString( "Java" ));cJSON_AddItemToArray(cjson_skill, cJSON_CreateString( "Python" ));cJSON_AddItemToObject(cjson_test, "skill", cjson_skill);/* 添加一个值为 False 的布尔类型的JSON数据(添加一个链表节点) */cJSON_AddFalseToObject(cjson_test, "student");/* 打印JSON对象(整条链表)的所有数据 */str = cJSON_Print(cjson_test);printf("%sn", str);/* 释放整条链表内存 */cJSON_Delete(cjson_test);return 0;
}int standard_app_demo_main()
{osal_task_create("cjson_print_demo",cjson_print_demo_entry,NULL,0x800,NULL,2);return 0;
}
在user_demo.mk中配置文件路径:
#example for cjson_print_demoifeq ($(CONFIG_USER_DEMO), "cjson_print_demo") user_demo_src = ${wildcard $(TOP_DIR)/targets/STM32L431_BearPi/Demos/cloud_test_demo/cjson_print_demo.c}endif
位置如下:
然后在.sdkconfig
中开启cJSON组件,并且选中该demo:
实验结果如图:
5. cJSON数据解析
解析方法
解析JSON数据的过程,其实就是剥离一个一个链表节点(键值对)的过程。
解析方法如下:
- ① 创建链表头指针:
cJSON* cjson_test = NULL;
- ② 解析整段JSON数据,并将链表头结点地址返回,赋值给头指针:
解析整段数据使用的API只有一个:
(cJSON *) cJSON_Parse(const char *value);
- ③ 根据键值对的名称从链表中取出对应的值,返回该键值对(链表节点)的地址
(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);
- ④ 如果JSON数据的值是数组,使用下面的两个API提取数据:
(int) cJSON_GetArraySize(const cJSON *array);
(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index);
解析示例
下面用一个例子来说明如何解析出开头给出的那段JSON数据。
在存放示例文件的文件夹cloud_test_demo
中,再新建一个实验文件cjson_parse_demo.c
,编写如下代码:
#include <osal.h>
#include <stdio.h>
#include <cJSON.h>char *message =
"{ "name":"mculover666", "age": 22, "weight": 55.5, "address": { "country": "China","zip-code": 111111}, "skill": ["c", "Java", "Python"],"student": false
}";static int cjson_test1_demo_entry()
{cJSON* cjson_test = NULL;cJSON* cjson_name = NULL;cJSON* cjson_age = NULL;cJSON* cjson_weight = NULL;cJSON* cjson_address = NULL;cJSON* cjson_address_country = NULL;cJSON* cjson_address_zipcode = NULL;cJSON* cjson_skill = NULL;cJSON* cjson_student = NULL;int skill_array_size = 0, i = 0;cJSON* cjson_skill_item = NULL;/* 解析整段JSO数据 */cjson_test = cJSON_Parse(message);if(cjson_test == NULL){printf("parse fail.n");return -1;}/* 依次根据名称提取JSON数据(键值对) */cjson_name = cJSON_GetObjectItem(cjson_test, "name");cjson_age = cJSON_GetObjectItem(cjson_test, "age");cjson_weight = cJSON_GetObjectItem(cjson_test, "weight");printf("name: %sn", cjson_name->valuestring);printf("age:%dn", cjson_age->valueint);printf("weight:%.1fn", cjson_weight->valuedouble);/* 解析嵌套json数据 */cjson_address = cJSON_GetObjectItem(cjson_test, "address");cjson_address_country = cJSON_GetObjectItem(cjson_address, "country");cjson_address_zipcode = cJSON_GetObjectItem(cjson_address, "zip-code");printf("address-country:%snaddress-zipcode:%dn", cjson_address_country->valuestring, cjson_address_zipcode->valueint);/* 解析数组 */cjson_skill = cJSON_GetObjectItem(cjson_test, "skill");skill_array_size = cJSON_GetArraySize(cjson_skill);printf("skill:[");for(i = 0; i < skill_array_size; i++){cjson_skill_item = cJSON_GetArrayItem(cjson_skill, i);printf("%s,", cjson_skill_item->valuestring);}printf("b]n");/* 解析布尔型数据 */cjson_student = cJSON_GetObjectItem(cjson_test, "student");if(cjson_student->valueint == 0){printf("student: falsen");}else{printf("student:errorn");}/* 释放整条链表内存 */cJSON_Delete(cjson_test);return 0;
}int standard_app_demo_main()
{osal_task_create("cjson_test1_demo",cjson_test1_demo_entry,NULL,0x800,NULL,2);return 0;
}
在user_demo.mk中配置文件路径:
#example for cjson_parse_demoifeq ($(CONFIG_USER_DEMO), "cjson_parse_demo") user_demo_src = ${wildcard $(TOP_DIR)/targets/STM32L431_BearPi/Demos/cloud_test_demo/cjson_parse_demo.c}endif
位置如下:
然后在.sdkconfig
中开启cJSON组件,并且选中该demo:
实验结果如图:
注意事项
在本示例中,因为我提前知道数据的类型,比如字符型或者浮点型,所以我直接使用指针指向对应的数据域提取,在实际使用时,如果提前不确定数据类型,应该先判断type的值,确定数据类型,再从对应的数据域中提取数据。
6. cJSON使用过程中的内存问题
内存及时释放
cJSON的所有操作都是基于链表的,所以cJSON在使用过程中大量的使用malloc
从堆中分配动态内存的,所以在使用完之后,应当及时调用下面的函数,清空cJSON指针所指向的内存,该函数也可用于删除某一条数据:
(void) cJSON_Delete(cJSON *item);
注意:该函数删除一条JSON数据时,如果有嵌套,会连带删除。
内存钩子
cJSON在支持自定义malloc函数和free函数,方法如下:
- ① 使用
cJSON_Hooks
来连接自定义malloc函数和free函数:
typedef struct cJSON_Hooks
{/* malloc/free are CDECL on Windows regardless of the default calling convention of the compiler, so ensure the hooks allow passing those functions directly. */void *(CJSON_CDECL *malloc_fn)(size_t sz);void (CJSON_CDECL *free_fn)(void *ptr);
} cJSON_Hooks;
- ② 初始化钩子cJSON_Hooks
(void) cJSON_InitHooks(cJSON_Hooks* hooks);
cjson使用_LiteOS云端对接教程01-cJSON组件使用教程相关推荐
- primefaces教程_Primefaces BlockUI组件示例教程
primefaces教程 Primefaces BlockUI is used to block interactivity of JSF components with optional ajax ...
- primefaces教程_Primefaces FileUpload组件示例教程
primefaces教程 Today we will look into the Primefaces FileUpload component. HTML provides you file inp ...
- primefaces教程_Primefaces仪表板组件示例教程
primefaces教程 We've mentioned earlier, Primefaces is one of leading libraries that provide you set of ...
- primefaces教程_Primefaces日历组件示例教程
primefaces教程 In our previous tutorials, we've covered several types of Primefaces components such as ...
- 原创教程之——reactjs 组件入门教程
在学习react之前,希望你有以下准备: react的安装 ECMAScript 6基础 本文不讲解react的安装步骤,若需了解请移步官方网站(https://reactjs.org/),那里讲解非 ...
- 物联网平台搭建教程01
物联网平台搭建教程01 1 物联网设备如何接入到网络? 2 网络通信方式 3 物联网设备之间,设备与云平台能够交换数据后,接下来要干怎么呢? 4 如何搭建起一个物联网系统框架呢?它的技术架构又是怎么样 ...
- QIIME 2教程. 01简介和安装 Introduction Install(2020.11开始更新)
写在前面 QIIME是微生物组领域最广泛使用的分析流程,10年来引用20000+次,2019年Nature杂志评为近70年来人体菌群研究的25个里程碑事件--里程碑16:生物信息学工具助力菌群测序数据 ...
- H2O Wave教程---基于浏览器的实时显示工具---教程01
H2O Wave教程-基于浏览器的实时显示工具-教程01 0 写在前面 1 开始-动手操作起来 2 分类:一个是脚本,一个是app 3 脚本怎么写 0 写在前面 总结一下自己学习H2Owave的学习情 ...
- PHPwind9.01傻瓜图解安装教程
PHPwind9.01傻瓜图解安装教程 大家好,按照惯例,PHPwind出新版本,我就会习惯发布图解安装教程,现在开始了~ 首先, 1.您的空间要支持php+mysql, 2.您的m ...
最新文章
- android程序安装后图标不显示
- BCH压力测试悄然开始?有优势但也有不足!
- iOS学习资源(三)
- 电脑常见故障处理_关于密封仪、密封试验仪器在使用上的常见故障及维护方面...
- Centos7 yum安装Python3.6环境,超简单
- [BUUCTF-pwn]——picoctf_2018_echo back
- python类的空间问题及类之间的关系
- exp导出表结构,不导出表数据。
- Boost组件lexical_cast
- Java的throws Exception
- eo是什么?一份没有满分的试题
- B2B供应链管理平台主流技术架构方案
- 惯性导航技术, IMU, AHRS
- c语言打印星号金字塔图形
- html中背景简写,css中background简写属性
- OneDrive教育版注册和登录
- IBM X3530 M4 RAID 卡驱动下载
- 路由器经常断网,网络不稳定怎么办, 教你如何让旧路由器也能运转飞起
- 安装cartographer出现的问题及解决方法
- WebView 生成长图,截图
热门文章
- python中字符串的几种表达方式(用什么方式表示字符串)
- Python 中的高级斗技,让函数返回结果的技巧
- 用 Python 快速实现 HTTP 和 FTP 服务器
- java创建阻塞_如何从HttpsURLConnection创建Java非阻塞InputStream?
- 深度学习中batch_size、epoch和iteration的含义
- python opencv cv2.resize()函数
- python 原始字符串r的用法
- 为什么不使用volatile,其它线程也能得到当前线程修改后的值,不使用volatile也不存在可见性问题?原来解决可见性问题不一定需要volatile,println也可以
- 变异蛮牛 树,dfs,二分图染色 牛客白月赛44
- rust编程之道 pdf_LPC: 想在内核里引入Rust,还需要做很多决定