当前位置:   article > 正文

cJSON的简单使用——STM32移植_stm32f7移植cjson

stm32f7移植cjson

目录

 

背景知识   

JSON数据结构

cJSON重要接口函数

解析案例

移植注意 事项


背景知识   

JSON是一种轻量级的数据交换格式,这里不做详细的分析,简单的理解为,是互联网上的一种数据打包协议,比较方便人阅读和编写,下面是阿里云物联网设备影子信息的json格式,如下所示:

  1. {
  2. "state": {
  3. "reported": {
  4. "hz": 20,
  5. "temp_comp": -0.5
  6. },
  7. "desired": {
  8. "hz": 5,
  9. "temp_comp": 10
  10. }
  11. },
  12. "metadata": {
  13. "reported": {
  14. "hz": {
  15. "timestamp": 1559705221
  16. },
  17. "temp_comp": {
  18. "timestamp": 1559705221
  19. }
  20. },
  21. "desired": {
  22. "hz": {
  23. "timestamp": 1560143969
  24. },
  25. "temp_comp": {
  26. "timestamp": 1560143969
  27. }
  28. }
  29. },
  30. "timestamp": 1560143969,
  31. "version": 0
  32. }

   如果仔细看,其实各个数据关系还是比较明显的。在json中,一切都是对象(object),因此任何支持的数据类型,都是可以通过JSON来表示的,例如字符串、数字、对象、数组。JSON本质是一个字符串,而json的对象则是其中的元素:

  1. var obj = {a:'hello', b: 'world'}; //这是一个对象,注意键名也是通过引号包裹
  2. var json = '{"a": "hello", "b":"world"}'; //这是一个JSON字符串,本质上是一个字符串

 再 专业的json知识点,这里就先不讲了,毕竟本文的主要目的是讲述cJSON。

    从上面的分析,我们可以知道,对于json的解析,简单的讲就是从 一堆字符串中,筛选出自己需要的信息。这个听起来很简单,真正实现起来确不是那么简单,因为字符串筛选功能本身就很难,再 加上其中的对象排列顺序可能不同,中间有没有空格,数值是 整数、还是浮点数,是正数,还是负数,这些都是很麻烦的,如果我们的数据格式是固定的还好,但是稍微有一点变动,就会很麻烦,所以写出一个兼容性很强的json解析程序, 还是比较难的,而cJSON就是一个专门用来解析JSON字符串的,因为简洁又简单,效率还快,移植也特别方便,只需要一个cJSON.c和cJSON.h文件。

    我们先说一下json常用的概念。

JSON数据结构

  1. /* The cJSON structure: */
  2. typedef struct cJSON
  3. {
  4. struct cJSON *next;
  5. struct cJSON *prev; //双向链表指针,用于遍历数组或对象链的向前、向后的指针
  6. struct cJSON *child; //数组或子对象节点
  7. int type; //key 的类型(要解析的目标string类型)
  8. char *valuestring; //字符串值
  9. /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
  10. int valueint; //整数值
  11. double valuedouble; //浮点数值
  12. char *string; //要解析的目标string
  13. } cJSON;

  说明:

1. cJSON数据结构,是采用双向链表来存储数据的,访问方式像一颗树,每一个节点都可以有兄弟节点, 通过next/prev指针来查找, 每个节点也可以有子节点,通过child指针来访问,进入下一层,只有节点是对象或者数组时,才可以有子 节点。这其实是表示,json格式可以包裹多层,如果我们要查找的数据在最里层,就需要一层一层的查找,比如上面我们举例说的阿里云物联网影子信息,最外层是整个json对象, 我们可以认为是“根”,这个“根”有多个子节点:state、metadata、timestamp、version。

子节点state又包含reported和desired子节点,而desired节点又有 hz和temp_comp子节点,所以我们遍历的时候,可以一层一层的剥开分析。后面的代码有详细的分析。

2、type是 要解析的目标string的 类型,类型具体种类如下:

  1. cJSON_Invalid
  2. cJSON_False
  3. cJSON_True
  4. cJSON_NULL
  5. cJSON_Number
  6. cJSON_String
  7. cJSON_Array
  8. cJSON_Object
  9. cJSON_Raw

  具体详细介绍,参考官方手册里的介绍,简单的讲,如果是Number类型,则valueint或valuedouble中存着对应值。若期望是int,则访问valueint,若期望是float,则访问valuedouble。

    若是String类型,则valueString中存放着值。

cJSON重要接口函数

cJSON *cJSON_Parse(const char *value);

 功能:用于解析JSON数据包,按照cJSON结构体的机构序列化整个数据包,其实这是使用cJSON解析功能的第一条指令,我们需要将一个字符串作为参数传递给该函数,也就是value,然后该函数会是使用malloc申请一块内存区域,大小为cJSON结构体,存放该字符串,后面所有的操作,都是根据这条命令的返回作为“根”对象。

参数:*value——要解析的字符串。

返回: cJSON结构体指针,指向我们 解析的字符串,所以我们需要对该返回进行判断,如果是NULL,则失败。

cJSON *cJSON_GetObjectItem(cJSON *object, const char *string);

   功能:获取json指定对象成员,string就是我们 想要筛选的对象名。

参数: object:cJSON对象,既可以是cJSON_Parse返回的“根”对象,也可以是子对象。

           string:要获取的指定对象名称。

返回值:要获取的指定对象句柄。当然类型是cJSON指针,就是前面提到的child指针。

void cJSON_Delete(cJSON *c);

  用来释放cJSON_Parse函数获取的句柄,释放整个内存,用在解析完成后调用。

 特别注意:cJSON的解析会伴随这malloc申请内存,我们在解析完成后,务必进行delete, 否则会造成内存溢出,程序死机。

解析案例

    下面我们就对前面举例的json字符串进行接下,代码如下:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <cJSON.h>
  4. int main(int argc, char *argv[]) {
  5. const char *line = "{\"state\": {\"desired\": {\"hz\": 5,\"temp_comp\": -0.1}}, \
  6. \"metadata\": {\"reported\": {\"hz\": {\"timestamp\": 1559720433}, \
  7. \"temp_comp\": {\"timestamp\": 1559720433}}, \
  8. \"desired\": {\"hz\": {\"timestamp\": 1559720433},\"temp_comp\": {\"timestamp\": 1559720433}}}, \
  9. \"timestamp\": 1559720433, \
  10. \"version\": 3}";
  11. cJSON *json;
  12. //char *out;
  13. json = cJSON_Parse( line ); //
  14. if(json == NULL)
  15. printf("json fmt error:%s\n.", cJSON_GetErrorPtr());
  16. else{
  17. cJSON *object = cJSON_GetObjectItem(json, "state");
  18. cJSON *object1 = cJSON_GetObjectItem(object, "desired");
  19. cJSON *item = cJSON_GetObjectItem(object1, "hz");
  20. printf("desired->hz: %d\n", item->valueint);
  21. item = cJSON_GetObjectItem(object1, "temp_comp");
  22. printf("desired->temp_comp: %f\n", item->valuedouble);
  23. cJSON_Delete(json);
  24. }
  25. return 0;
  26. }

   程序运行结果:

  从上面的代码可以验证前面说的,每个cJSON数据结构中,又有了child字节点,我们可以逐层进行剥开,来获取我们想要的数据。

移植注意 事项

   cJSON是非常轻量级的,我们只需要将cJSON.c和cJSON.h加入到我们的工程中即可,然后包含cJSON.h就可以了。唯一需要注意的就是,由于cJSON会频繁的调用malloc,也就是会申请内存,所以我们的程序中,有2方面 需要注意:

(1)解析完成后,一定要调用cJSON_Delete释放掉cJSON_Parse生成的句柄,也就是释放内存。

(2)在keil或者IAR中,设置“堆Heap”的空间尽量大些, 参考网上的推荐,设置为4096。其实如果只是筛选,只要我们及时delete,不修改也行。

         在IAR中设置Heap的方式是,Project->options->linker->config->Override default->Edit->Stack/Heap Sizes

         在Keil中,应该是在startup.s文件中,找对应Heap的 定义字段修改即可。

小结:上面的分析只是其中一种JSON格式,JSON格式还有其他的类型,cJSON都有具体的API接口函数,这里这里就不赘述了,还是看官方文档吧。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/神奇cpp/article/detail/908196
推荐阅读
相关标签
  

闽ICP备14008679号