cJSON的简单使用——STM32移植

目录

 

背景知识   

JSON数据结构

cJSON重要接口函数

解析案例

移植注意 事项


背景知识   

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

{
  "state": {
    "reported": {
      "hz": 20,
      "temp_comp": -0.5
    },
    "desired": {
      "hz": 5,
      "temp_comp": 10
    }
  },
  "metadata": {
    "reported": {
      "hz": {
        "timestamp": 1559705221
      },
      "temp_comp": {
        "timestamp": 1559705221
      }
    },
    "desired": {
      "hz": {
        "timestamp": 1560143969
      },
      "temp_comp": {
        "timestamp": 1560143969
      }
    }
  },
  "timestamp": 1560143969,
  "version": 0
}

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

var obj = {a:'hello', b: 'world'};   //这是一个对象,注意键名也是通过引号包裹

var json = '{"a": "hello", "b":"world"}'; //这是一个JSON字符串,本质上是一个字符串

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

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

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

JSON数据结构

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

  说明:

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

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

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

cJSON_Invalid
cJSON_False
cJSON_True
cJSON_NULL
cJSON_Number
cJSON_String
cJSON_Array
cJSON_Object
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字符串进行接下,代码如下:

#include 
#include 
#include 

int main(int argc, char *argv[]) {
	const char *line = "{\"state\": {\"desired\": {\"hz\": 5,\"temp_comp\":   -0.1}}, \
    \"metadata\": {\"reported\": {\"hz\": {\"timestamp\": 1559720433}, \
    \"temp_comp\": {\"timestamp\": 1559720433}}, \
    \"desired\": {\"hz\": {\"timestamp\": 1559720433},\"temp_comp\": {\"timestamp\": 1559720433}}}, \
  	\"timestamp\": 1559720433, \
  	\"version\": 3}";

    cJSON *json;
    //char *out;

    json = cJSON_Parse( line );			//

    if(json == NULL)
    	printf("json fmt error:%s\n.", cJSON_GetErrorPtr());
    else{
    	cJSON *object = cJSON_GetObjectItem(json, "state");
    	cJSON *object1 = cJSON_GetObjectItem(object, "desired");

    	cJSON *item = cJSON_GetObjectItem(object1, "hz");
    	printf("desired->hz: %d\n", item->valueint);

    	item = cJSON_GetObjectItem(object1, "temp_comp");
    	printf("desired->temp_comp: %f\n", item->valuedouble);

    	cJSON_Delete(json);	
	}

	return 0;
}

   程序运行结果:

  从上面的代码可以验证前面说的,每个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接口函数,这里这里就不赘述了,还是看官方文档吧。

你可能感兴趣的:(C/C++)