0x07和0x08 LiteOS_Lab仓库组件详解 cJSON
【摘要】 摘要:我们将设备采集到的数据上传到云平台一般有两种主流的方式:二进制码流和json,本章用于讲解在LiteOS仓库中集成的cJson库,用于将需要上报的数据封装为json格式或者将云平台下发的json格式数据解析出来。一、华为云平台需要的json数据格式当我们下位机使用mqtt协议来进行通讯并且mcu的ram和flash也比较充裕,我们就可以采用json格式来和云平台进行数据交互。首先在定义...
摘要:我们将设备采集到的数据上传到云平台一般有两种主流的方式:二进制码流和json,本章用于讲解在LiteOS仓库中集成的cJson库,用于将需要上报的数据封装为json格式或者将云平台下发的json格式数据解析出来。
一、华为云平台需要的json数据格式
当我们下位机使用mqtt协议来进行通讯并且mcu的ram和flash也比较充裕,我们就可以采用json格式来和云平台进行数据交互。
首先在定义产品的时候协议类型选择MQTT和数据格式选择JSON:
大家可以查看下这个帮助手册,主要说明了华为云平台要求的JSON数据格式的要求,JSON数据格式分为设备命令、设备消息和设备属性,我这里主要说的是设备属性和设备命令:设备属性上报、设备命令下发。
services:类型为list(array),存放设备服务数据
service_id:类型为string,与我们产品中的服务相对应Agriculture
properties:类型为object,存放设备服务的属性,例如Temperature、Humidity等,和属性名称及值对应,在产品中定义的,如下图。
云平台收到的数据必须与我们在产品中定义的数据格式相同,必选的属性必须存在,否则该数据会被丢弃,如果收到的数据正确我们就可以在“设备”页面中看到如下显示:
二、cJSON库简介
到这里大家应该明白了为什么今天我们需要讲解cJson库了,cJson库的作用是什么了。
cJSON库是一个基于MIT的代码,项目仓库地址:https://github.com/DaveGamble/cJSON,我们在LiteOS中主要用到其中的两个文件:cJSON.h和cJSON.c。
该库可以十分方便的将我们的数据结构体,例如:
转换为json格式的,甚至还能直接以非格式化字符的形式打印出来,就是这样的:
,极大的提高了我们处理数据的效率,还能将json格式的数据转换为便于处理的键、值等等。
三、cJson库中封装相关函数详解
这里就以上面的Agriculture的上报属性数据的结构体为例,带领大家将它转换为JSON格式的数据。
几个重要概念
Object和Item:Object和Item是相对的,不是绝对的,这句话有些抽象,我来举个例子向大家说明,例如之前我的定义的json数据:
1、 ①到⑩之间的所有数据可以称为一个Object,②到⑩之间的数据就是它的键为”services”,类型为array的item。
2、 ②到⑩之间的所有数据可以称为一个Object,其中③处的键值对是它的第一个键为“service_id”,类型为string的item,其中④到⑨处的键值对是它的第二个键为“properties”,类型为Object的item。
3、 ④到⑧之间的所有数据可以称为一个Object,其中⑤⑥⑦都是分别是该Object的三个item,他们的类型都是int。
嵌套:嵌套的意思大家只要理解了上面我说的Object和Item的概念就算理解了。Object中嵌套了Object,那这个被嵌套的Object就是Item。
cJSON结构体
该结构体的声明在cJSON.h文件中,大家可以自行查看。
next和prev指针:通过cJSON结构体定义的变量可以实现一个链表,在进行增、删、查、改时通过next和prev指针来完成。
child指针:当我们需要添加一个item到某个Object的时候(嵌套),就将这个item指针挂接到该节点的child成员上。
type变量:用于标记该item的类型,例如string、number、bool、array、object等。
valuestring指针:用于当item类型为string类型时,指向值所对应的字符串。
valueint变量:用于当item类型为number类型时,存储值对应的int类型数据。
Valuedouble变量:用于当item类型为number类型时,存储值对应的double类型数据。
string指针:指向该item键的对应的名称。
当该item为bool类型,其中的值为false或者true时,该值存储于type变量处。
绑定系统特有的内存分配及释放函数
因为在创建object和删除object时需要使用到内存,所以在初始化cJSON库时,我们就需要将系统中进行内存分配及释放的函数注册到cJSON库中。
例如裸机开发时,用malloc分配内存,用free释放内存。
例如用LiteOS_Lab框架开发时,用osal_malloc分配内存,用osal_free释放内存。
通过如下操作即可完成注册:
Object的创建
我们需要进行json格式数据处理时,首先通过JSON数据类型定义一个JSON数据类型变量指针,并通过cJSON_CreateObject为其分配内存并初始化(如果是array类型的object则调用cJSON_CreateArray,其他类型的创建可以自己类比,创建相关函数见下图)。
向Object中添加item
例如我们要创建如下的一个JSON格式的数据。
首先需要创建两个Object,第一个称为root的Object,后面将键为properties的item挂上去,第二个为properties的Object,用于“温度、湿度、光照”键值对的挂接。
代码部分:
unsigned char* str_js = NULL; //定义一个字符串指针
cJSON* cjson_root = NULL; //定义cJSON类型的指针
cJSON* cjson_properties = NULL;
cjson_root = cJSON_CreateObject(); //为上面的指针分配内存并进行初始化
cjson_properties = cJSON_CreateObject();
//将温度、湿度、光照键值对添加到名为properties的Object中
cJSON_AddNumberToObject(cjson_properties, "Temperature", 20);
cJSON_AddNumberToObject(cjson_properties, "Humidity", 18);
cJSON_AddNumberToObject(cjson_properties, "luminance", 50);
//将名为cjson_data的Object挂接到名为cjson_root的Object上
cJSON_AddItemToObject(cjson_root, "properties", cjson_properties);
//将cJSON转换为非格式化字符串打印一下
str_json = cJSON_Print(cjson_root);
printf("Data is %s\r\n", str_json);
//用完之后一定要记得释放
cJSON_Delete(cjson_root);
if(str_json!=NULL)
osal_free(str_json);
效果图如下:
解析云平台下发的JSON格式指令
假设我们收到了一帧JSON格式来自服务器的数据,如下图:
从JSON的字面意思可以理解:该数据是一个命令,命令的键为“Light”,值为“ON”,命令单片机将板载LED灯打开。
我们可以这样理解这个数据,”paras”、”service_id”和”command_name”都术语同一个object中的item,我们暂时可以忽略”paras”和”service_id”参数,先提取paras这个object类型的item,然后读取”paras”中的item的键是否包含”Light”,如果包含再读取”Light”键对应的值为ON还是OFF。
具体步骤可以分为:
① 将MQTT接收任务中收到的“string“类型的数据转换为JSON格式
② 读取object中的paras项目,并查询其中是否有特定名称的键(Light)
③ 提取特定名称键的值
④ 根据②、③步骤中获取的键、值来执行相应操作
⑤ 上报处理结果
将MQTT接收任务中收到的数据解析为JSON格式
首先创建一个cJSON类型的结构体指针,用于保存将MQTT接收任务中收到的“string“类型的数据转换为JSON格式的数据
调用CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value);函数,传入MQTT接收任务中收到的“string“类型的数据指针,解析完成之后可以收到一个cJSON类型的结构体指针,使用完之后记得释放,否则会造成内存泄漏。
从object中读取item
将解析出来的JSON结构体指针传入作为第一个参数,”paras”作为第二个参数传入CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string);函数,将得到一个cJSON类型的结构体指针,该指针的内容就是名为”paras”的item。
将名为”paras”的item cJSON结构体指针作为第一个参数,“Light”作为第二个参数传入CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string);函数,如果paras中包含名为”Light”的item,则会放回true,这是我们可以通过if语句进行判断,如果存在,则进行读取名为”Light”的键对应的值。
从object中读取值
将指向名为”Light”的item的cJSON函数指针传入CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item);即可在函数返回值得到一个字符串指针,我们可以通过strstr等等之类的字符串比较函数判断该返回值是否为”ON”或者”OFF”,并指示单片机完成相应操作即可。
总结以及注意事项:cJSON库的操作到这里就大概说完了,大家还可以自行查阅cJSON.h文件,里面定义了很多函数和结构体,可以根据自己需要进行的操作调用相应的函数来处理,唯一需要注意的是无论是创建Object还是解析string,用完之后一定要记得释放内存,如果使用了例如将JSON格式的数据生成为非格式化字符串的函数一定要记得释放这个string字符串指针对的的内存,否则最终会造成内存泄露,导致程序出错。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
作者其他文章
评论(0)