来点儿干货,今天白天主要时间都在车上,于是继续完善这篇文章。
原本以为在写上一条引用的时候可以把这篇完成,结果中途又遇到了一些问题,自己对JSON的语法规则并没有很理解,这是停留在应用层面。于是,在业余时间,挤了挤时间,看了一些JSON的语法知识。
前言
- 写技术文章确实是一件很难的事情,写出来容易,要自己理解,同时还要让读者看懂,真的是一件很不容易的事情。与君共勉。
1 概述
- 物联网产品免不了和服务器做交互,目前使用的比较多的是以太网接网线或者WiFi设备或者GPRS。
- 无论是用的透传,还是非透传的方式,都需要将硬件产生的原始数据转换为服务器端的物联网数据,硬件处理服务端的物联网数据时,也需要转化为十六进制的数据,方便处理。
- 这里涉及到了两个问题,JSON数据包的解析和组装。
- 硬件设备使用的基本都是C语言,同时考虑成本等因素,单片机的RAM也是KB级的比较多。
- 此文章重点讲解一下一个非常好用的CJSON库,在MDK中编译的代码量只有十几KB,占用的RAM只有几KB
- 该JSON库具有占用空间小(ROM占用10K以内,易移植(只有一个c文件和一个h文件),使用简单(只需要掌握几个常用的函数,和一个CJSON结构体就可以轻松地使用)的特点。包含到项目源码中非常方便,而且其实现效率也是非常高的。
2 JSON
- 先贴一段JSON数据,阅读下文时可以参考一下对应的数据格式
{
"msg": [
{
"ctime": "20170916091927",
"gateLine": "ON",
"locksId": 65,
"isLocked": true,
"locksNo": "1001001105",
"pwd": {
"1": "654321",
"2": "147258"
}
},
{
"ctime": "20170916091927",
"gateLine": "ON",
"locksId": 66,
"isLocked": false,
"locksNo": "1001001106",
"pwd": {
"1": "123456",
"2": “147258"
}
}
],
"code": 0
}
- 下文对JSON的介绍来自笔者看了大量教程以后的总结。
2.1JSON 语法规则
JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。
- 结构特点
- 数据在名称/值对中
- 数据由逗号分隔
- 大括号保存对象
- 中括号保存数组
- 键、值可层层嵌套
- 说明一下:键、值可层层嵌套
- 理解该特点是运用JSON的关键。
- JSON的值可以为以下类型:
数字(整数或浮点数)
字符串(在双引号中)
逻辑值(true 或 false)
数组(在方括号中)
对象(在花括号中)
null(在不同的语言表示的意义不一样,开发者只需要知道在自己的语言里的意义就可以了) - JSON的值为数组时,数组可以是数字数组,字符串数组,对象数组等,所以该值里面又会包含很多键值对
- 解析JSON的核心也是一层一层地往外拨,反复地调用一个或者两个方法(下文会详细介绍)
2.2 JSON值
2.2.3 JSON数字
"locksId": 65,
2.2.4 JSON字符串
"ctime": "20170916091927",
2.2.5 JSON逻辑值
"isLocked": false,
2.2.6 JSON数组
- "msg"对应的值为一个数组,数组里面有两个对象(花括号)元素,
"msg": [
{
"ctime": "20170916091927",
"gateLine": "ON",
"locksId": 65,
"isLocked": true,
"locksNo": "1001001105",
"pwd": {
"1": "654321",
"2": "147258"
}
},
{
"ctime": "20170916091927",
"gateLine": "ON",
"locksId": 66,
"isLocked": false,
"locksNo": "1001001106",
"pwd": {
"1": "123456",
"2": “147258"
}
}
],
2.2.7 JSON对象
- 一个对象里面一般会包含多种类型的值,里面的“pwd”对应的值也是一个对象
{
"ctime": "20170916091927",
"gateLine": "ON",
"locksId": 66,
"isLocked": false,
"locksNo": "1001001106",
"pwd": {
"1": "123456",
"2": “147258"
}
}
看到,应该可以深入感受一下上文提到的"键、值可层层嵌套"
2.2.8 介绍一下JSON数据格式
- Object,Item,Array
笔者需要补充一下json的数据格式,学习中。。。。。。
- 该小节作为一章的内容放在了第2章中。
3 介绍CJOSN的结构
- 核心结构体
/* The cJSON structure: */
typedef struct cJSON {
struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
int type; /* The type of the item, as above. */
char *valuestring; /* The item's string, if type==cJSON_String */
int valueint; /* The item's number, if type==cJSON_Number */
double valuedouble; /* The item's number, if type==cJSON_Number */
char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
} cJSON;
这个结构体包含了所有的返回数据类型。
4 介绍CJSON的解析
4.1 解析使用的核心函数如下
cJSON *cJSON_Parse(const char *value);
void cJSON_Delete(cJSON *c);
int cJSON_GetArraySize(cJSON *array);
cJSON *cJSON_GetArrayItem(cJSON *array,int item);
cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);
- 一共五个函数, 前三个函数使用的位置和方法都很固定,后两个就是在获取数据的时候反复调用,目的就是走到JSON数据的最底部--获取健值。
- 总结一下就是:三个函数固定使用,目的是获取待处理的数据或者做收尾工作;两个函数反复使用,目的是一层一层往下,获取到基本数据。
cJSON *cJSON_Parse(const char *value);
- 该函数是接收到json字符串时,将字符串转为json格式的字符串,一般都是在解析的第一行主要代码的位置。
void cJSON_Delete(cJSON *c);
- 该函数是在解析完成以后,用于释放内存。
int cJSON_GetArraySize(cJSON *array);
- 该函数是在有数组的时候,且不知道数组长度的情况下,获取数组的长度,然后再用循环挨个解析数组。如果需要解析的数据里面没有数组的话,或者可以知道数组的具体长度的话,就不需要使用该函数。
- 下面是两个最重要的函数,熟练掌握了这两个函数,大多数的场景都可以应对了。
cJSON *cJSON_GetArrayItem(cJSON *array,int item);
- 该函数的作用是获取数组里面的元素,元素可以是对象,字符串,数值等各种类型。
cJSON *cJSON_GetObjectItem(cJSON *object,const char *string);
- 该函数的作用是获取对象里面,键对应的值。
4.2 解析举例说明
4.2.1数据分析
- 还是用上面的的例子,贴下源数据
{
"msg": [
{
"ctime": "20170916091927",
"gateLine": "ON",
"locksId": 65,
"isLocked": true,
"locksNo": "1001001105",
"pwd": {
"1": "654321",
"2": "147258"
}
},
{
"ctime": "20170916091927",
"gateLine": "ON",
"locksId": 66,
"isLocked": false,
"locksNo": "1001001106",
"pwd": {
"1": "123456",
"2": “147258"
}
}
],
"code": 0
}
- 简单分析一下
分析务必搞清楚几点:
1.第二章里面介绍的结构
2.上文提到的五个方法,有不明白的可往上翻
- 最外围结构为一个对象(花括号)。使用cJSON_Parse就可以获取到JSON对象内容
- 对象当中包含了两个Item,分别是"msg"和"code"。
- "code"对应的是一个数字,一次解析可以获得数据。使用cJSON_GetObjectItem,获取返回的int类型数据
- "msg"里面包含了一个JSON数组,每个数组的元素为一个对象。使用cJSON_GetObjectItem,返回值是一个array
- 先获取到array的数量。使用cJSON_GetArraySize
- 用for循环对每个数组里面的对象元素做解析cJSON_GetArrayItem,返回一个对象
- 反复对对象做解析,就可以获取所有的数据。反复使用cJSON_GetObjectItem
4.2.2 贴一小段代码示例
- 笔者不喜欢贴代码,虽然很多读者都喜欢直接看代码
- 还是那句话,看代码引用起来快,但是后期维护更多的时间来搞清楚算法
- 下面提到的步骤1,2,3,4....对应上文简单分析的7个步骤,一一对应看即可。
步骤1
Mother_obj=cJSON_Parse(json_string);
//json_string为源数据的字符数组
//Mother_obj为整个花括号对应的对象
步骤2,3
arry_obj_item = cJSON_GetObjectItem(Mother_obj,"code");
json_int_buf=arry_obj_item->valueint;
//获取code的值
步骤4
array = cJSON_GetObjectItem(Mother_obj,"msg");
//获取“msg”的数组值
步骤5
lock_array_len = cJSON_GetArraySize(array);
//获取数组里面的元素个数
步骤6,7
for(i=0;i<lock_array_len;i++){
array_obj = cJSON_GetArrayItem(array,i);
//获取数组里面的元素对象
//后面就对对象做处理,反复调用cJSON_GetObjectItem即可
if(array_obj==NULL)continue;
//获取“time”对应的值
arry_obj_item = cJSON_GetObjectItem(array_obj,"ctime");
if(arry_obj_item==NULL)continue;
json_buf = arry_obj_item->valuestring;
//获取“lockId”对应的值
arry_obj_item = cJSON_GetObjectItem(array_obj,"locksId");
if(arry_obj_item==NULL)continue;
json_int_buf = arry_obj_item->valueint;
}
按照层层嵌套的逻辑,就可以解析出整个字符串
5 介绍CJSON的数据组合
- 下一篇继续介绍
CrekerLi,与君共勉于技术之路上