日常工作/学习中难免会遇到嵌入式设备与服务器交互的场景,通讯协议上基本以下几种
1.自定义字符,包头包尾
2.xml
3.json
第一种的话虽然程序写起来简单,但是存在 通用性差,而且出现问题,抓包时还要对照协议导致排查效率低下。
而第3种json,考虑到服务器大部分都是java spring应用,用阿里的fastjson库直接就可以构建出java实体类,所以嵌入式这边使用json来交互,对web后端开发人员更加友好。
所以今天就来研究研究json作为通讯协议的嵌入式应用。
我们用的json库是jansson,因为嵌入式这边,特别是在mcu上,资源紧缺啊,所以只能上一些优化过的库。
1.keil添加jansson
这个很简单,网上资源有很多,这边稍微带一下吧
然后就可以看到,lib和C文件添加进来了,这样环境就搭建好了
使用是,记得加个头文件即可
#include <string.h>
#include <jansson.h>
2.使用
我们使用json,无非2个需求:
发包和解包:把自己的数据打包以json格式发出去
把服务器(别人的)包接受进来,解析出相应的数字/数组/字符串/对象
jansson的API特比的丰富,,类型如下
JSON_OBJECT
JSON_ARRAY
JSON_STRING
JSON_INTEGER
JSON_REAL
JSON_TRUE
JSON_FALSE
JSON_NULL
当我们得到一个 json_t的数据结构,可以用判断其类型,例如判断出是字符串,接下来可以用
json_string_value(val)得到其数据,如果是integer类型的话,也一样,用json_is_integer(val)来判断,用json_integer_value(val)得到其数据
json_typeof(const json_t *json)
int json_is_object(const json_t *json)
int json_is_array(const json_t *json)
int json_is_string(const json_t *json)
int json_is_integer(const json_t *json)
int json_is_real(const json_t *json)
int json_is_true(const json_t *json)
int json_is_false(const json_t *json)
int json_is_null(const json_t *json)
2.1发包
对于业务来讲,json打包除了字符串,还有特殊的数组,对象等,这边给一个例子,这些类型在里面都有示例
代码:
void json()
{
json_t* myjson = NULL;
json_t* item = NULL;
json_t* array = NULL;
myjson = json_object();
item = json_object();
array = json_array();
char* str = NULL;
json_array_append(array,json_integer(21));
json_array_append(array,json_integer(128));
json_array_append(array,json_integer(0));
json_array_append(array,json_integer(128));
json_object_set_new(item,"heartbeat",json_string("on"));
json_object_set_new(item,"id",json_integer(21));
json_object_set_new(item,"true",json_true());
json_object_set_new(item,"false",json_false());
json_object_set_new(item,"json_real",json_real(12.987));
json_object_set_new(myjson,"distance",array);
json_object_set_new(myjson,"mything",item);
str = json_dumps(myjson, JSON_INDENT(0));
printf("str = %s \n",str);
free(str);
json_decref(myjson);
}
打印结果
{
"distance": [
21,
128,
0,
128
],
"mything": {
"heartbeat": "on",
"id": 21,
"true": true,
"json_real": 12.987,
"false": false
}
}
这个例子中可以看出,核心数据对象是json_t,
json_decref函数用于销毁构建json过程中申请的内存,不然存在 内存泄漏的风险。
json_dumps是打印函数,将json_t转换为字符串,其第二个形参决定了打印的格式,工作中常用的JSON_INDENT,JSON_COMPACT,JSON_PRESERVE_ORDER参数在json_pack函数的时候会提到。
2.2解包
对服务器过来的json数据进行解包,这个过程中如果可以的话,最好对解包后的数据做层校对,这样是最好了,这边同样给个例子,覆盖大部分业务场景用到的数据类型
void json_parse(){
char *strbuf="{\"distance\":[21, 128, 0, 128], \"mything\": {\"heartbeat\": \"on\",
\"id\": 21, \"true\": true, \"json_real\": 12.987, \"false\":
false}}";
json_error_t err;
char* str = NULL;
json_t* myjson = json_loads(strbuf, 0, &err);
if(NULL != myjson && json_is_object(myjson))
{
json_t* disancearray= json_object_get(myjson, "distance");
if(NULL != disancearray && json_is_array(disancearray))
{
size_t index;
json_t *value;
json_array_foreach(disancearray, index, value) {
/* block of code that uses index and value */
printf("%d:%d\r\n",index,(int)json_integer_value(value));
}
}
json_t* mything= json_object_get(myjson, "mything");
if(NULL != mything && json_is_object(mything))
{
json_t* heartbeat= json_object_get(mything, "heartbeat");
if(NULL != heartbeat && json_is_string(heartbeat)){
printf("heartbeat:%s\r\n",json_string_value(heartbeat));
}
json_t* id= json_object_get(mything, "id");
if(NULL != heartbeat && json_is_integer(id)){
printf("id:%d\r\n",(int)json_integer_value(id));
}
json_t* true= json_object_get(mything, "true");
if(NULL != heartbeat && json_is_true(true)){
printf("true:%d\r\n",json_is_true(true));
}
json_t* json_real= json_object_get(mything, "json_real");
if(NULL != heartbeat && json_is_real(json_real)){
printf("true:%f\r\n",json_real_value(json_real));
}
json_t* false= json_object_get(mything, "false");
if(NULL != heartbeat && json_is_false(false)){
printf("false:%d\r\n",json_is_false(false));
}
}
}
json_decref(myjson);
}
打印结果
0:21
1:128
2:0
3:128
heartbeat:on
id:21
true:1
true:12.987000
false:1
这段代码的话,json_loads函数比较重要,将字符串转换为json_t数据结构体,其取值过程基本类似,都是先判断一下是否是null,类型是否正确,然后用该类型的取值函数取值.
如果止步于此,就不是jansson了,下一章节,给大家介绍json_pack和json_unpack两个神兵利器