0x07和0x08 LiteOS_Lab仓库组件详解 cJSON

举报
樊心昊 发表于 2020/06/22 15:27:04 2020/06/22
【摘要】 摘要:我们将设备采集到的数据上传到云平台一般有两种主流的方式:二进制码流和json,本章用于讲解在LiteOS仓库中集成的cJson库,用于将需要上报的数据封装为json格式或者将云平台下发的json格式数据解析出来。一、华为云平台需要的json数据格式当我们下位机使用mqtt协议来进行通讯并且mcu的ram和flash也比较充裕,我们就可以采用json格式来和云平台进行数据交互。首先在定义...

摘要:我们将设备采集到的数据上传到云平台一般有两种主流的方式:二进制码流和json,本章用于讲解在LiteOS仓库中集成的cJson库,用于将需要上报的数据封装为json格式或者将云平台下发的json格式数据解析出来。

一、华为云平台需要的json数据格式

当我们下位机使用mqtt协议来进行通讯并且mcu的ram和flash也比较充裕,我们就可以采用json格式来和云平台进行数据交互。

首先在定义产品的时候协议类型选择MQTT和数据格式选择JSON:

image.png

大家可以查看下这个帮助手册,主要说明了华为云平台要求的JSON数据格式的要求,JSON数据格式分为设备命令、设备消息和设备属性,我这里主要说的是设备属性和设备命令:设备属性上报设备命令下发

image.png

services:类型为list(array),存放设备服务数据

service_id:类型为string,与我们产品中的服务相对应Agriculture

image.png

properties:类型为object,存放设备服务的属性,例如Temperature、Humidity等,和属性名称及值对应,在产品中定义的,如下图。

image.png

云平台收到的数据必须与我们在产品中定义的数据格式相同,必选的属性必须存在,否则该数据会被丢弃,如果收到的数据正确我们就可以在“设备”页面中看到如下显示:

image.png

二、cJSON库简介

到这里大家应该明白了为什么今天我们需要讲解cJson库了,cJson库的作用是什么了。

cJSON库是一个基于MIT的代码,项目仓库地址:https://github.com/DaveGamble/cJSON,我们在LiteOS中主要用到其中的两个文件:cJSON.h和cJSON.c。

该库可以十分方便的将我们的数据结构体,例如:

image.png

转换为json格式的,甚至还能直接以非格式化字符的形式打印出来,就是这样的:

image.png

,极大的提高了我们处理数据的效率,还能将json格式的数据转换为便于处理的键、值等等。

三、cJson库中封装相关函数详解

这里就以上面的Agriculture的上报属性数据的结构体为例,带领大家将它转换为JSON格式的数据。

几个重要概念

       Object和Item:Object和Item是相对的,不是绝对的,这句话有些抽象,我来举个例子向大家说明,例如之前我的定义的json数据:

image.png

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文件中,大家可以自行查看。

image.png

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释放内存。

通过如下操作即可完成注册:

image.png

Object的创建

我们需要进行json格式数据处理时,首先通过JSON数据类型定义一个JSON数据类型变量指针,并通过cJSON_CreateObject为其分配内存并初始化(如果是array类型的object则调用cJSON_CreateArray,其他类型的创建可以自己类比,创建相关函数见下图)。

image.png

向Object中添加item

例如我们要创建如下的一个JSON格式的数据。

image.png

首先需要创建两个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);

 

效果图如下:

image.png

 

解析云平台下发的JSON格式指令


假设我们收到了一帧JSON格式来自服务器的数据,如下图:

    image.png       

从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

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。