• 发文
  • 评论
  • 微博
  • 空间
  • 微信

ESP8266_16天气预报之JSON数据的生成与解析

单片机爱好者 2019-09-25 08:47 发文



        上一节通过HTTP的GET操作,我们获取了天气相关的信息。但相关信息是以JSON格式发下来的,所以需要对数据进行解析。解析之前先明确几点:


1、JSON是一种数据格式,不是通信协议。有专门的解析函数,但如果懒得用,可以用特定的方法获取想要的数据。

2、解析数据的前提是了解数据的结构,而不是说对一组结构陌生的数据进行解析。

以上一节得到的数据为例:

{"results":[{"location":{"id":"WX4BZD6KEJFY","name":"Langfang","country":"CN","path":"Langfang,Langfang,Hebei,China","timezone":"Asia/Shanghai","timezone_offset":"+08:00"},"now":{"text":"Overcast","code":"9","temperature":"20"},"last_update":"2019-09-19T20:15:00+08:00"}]}


这样看有点乱,整理一下:


{"results": [{"location": {"id": "WX4BZD6KEJFY","name": "Langfang","country": "CN","path": "Langfang,Langfang,Hebei,China","timezone": "Asia/Shanghai","timezone_offset": "+08:00"},"now": {"text": "Overcast","code": "9","temperature": "20"},"last_update": "2019-09-19T20:15:00+08:00"}]}


这样是不是整齐多了?

你会发现,这组数据貌似有个名字:results,它由三部分组成:location,now和last_update。也就是位置、当前天气和最后更新时间。剩下就是一些标点,可以最后考虑。

以“"id": "WX4BZD6KEJFY"”为例,前面的是“键”,后面的是“值”,合在一起,叫键值对。

可以看到,温度是27度。但程序没有眼睛,不能直接看,怎么办呢?偷懒的方式:从第一个符号“{”出现,计算数字“27”出现的位置,只要你的API链接不变,网站提供的数据格式不更新,一般来说也能行。

但这个方法有点low,所以,还是用JSON函数解析吧。

首先说一下,官方的SDK里面(例程IoT_Demo)提供了JSON相关的库,可以直接使用,所以这里的讲解主要参照官方,顺便百度了一下,如果有说的不对的地方,欢迎指正。

代码不少,但重复的比较多,所以理解起来不是很难。大致分6步:

1

添加头文件和相关源文件

这个简单,主要是user_json.c和user_json.h,放到指定位置。不会的,看我工程文件。

2

更新JSON数据

第二步就能获取数据了?

其实不是,因为数据的解析是一个整体的操作,这只是其中的一个环节。先看一下SDK中的例程文件user_webserver.c:

LOCAL int ICACHE_FLASH_ATTRlight_status_get(struct jsontree_context *js_ctx){    const char *path = jsontree_path_name(js_ctx, js_ctx->depth - 1);    if (os_strncmp(path, "red", 3) == 0) {        jsontree_write_int(js_ctx, user_light_get_duty(LIGHT_RED));    } else if (os_strncmp(path, "green", 5) == 0) {        jsontree_write_int(js_ctx, user_light_get_duty(LIGHT_GREEN));    } else if (os_strncmp(path, "blue", 4) == 0) {        jsontree_write_int(js_ctx, user_light_get_duty(LIGHT_BLUE));    }..........    return 0;}


因为篇幅原因,去掉了一些代码。从函数名字可以看出来,它有一个关键词“get”,也就是“获取”。所以,这个函数的功能是把解析好的数据更新到JSON结构里。

明白了函数的功能,先定义一个“location”的更新函数,代码如下:


int ICACHE_FLASH_ATTRjson_location_get(struct jsontree_context *js_ctx){    const char *path = jsontree_path_name(js_ctx, js_ctx->depth - 1);    if (os_strncmp(path, "id", os_strlen("id")) == 0) {     jsontree_write_string(js_ctx, "WX4BZD6KEJFY");    }    else if (os_strncmp(path, "name", os_strlen("name")) == 0) {     jsontree_write_string(js_ctx, "Langfang");    }    else if (os_strncmp(path, "country", os_strlen("country")) == 0) {     jsontree_write_string(js_ctx, "CN");    }    else if (os_strncmp(path, "path", os_strlen("path")) == 0) {     jsontree_write_string(js_ctx, "Langfang,Langfang,Hebei,China");    }    else if (os_strncmp(path, "timezone", os_strlen("timezone")) == 0) {         jsontree_write_string(js_ctx, "Asia/Shanghai");    }    else if (os_strncmp(path, "timezone_offset", os_strlen("timezone_offset")) == 0) {         jsontree_write_string(js_ctx, "+08:00");    }    return 0;}


同样的结构,同样的代码,就是把信息换一下。对比上下两个函数中的两行代码:


jsontree_write_int(js_ctx, user_light_get_duty(LIGHT_RED));jsontree_write_string(js_ctx, "WX4BZD6KEJFY");

第一行,因为要写入整形数据,所以使用函数“jsontree_write_int”。

第二行,因为要写入字符串,所以使用函数“jsontree_write_string”。

还有一点注意事项,第二行写入的数据是死的,即便后面更新了数据,新的数据也进不来。这里写成这样的方式是为了演示,理解方便。

同样的方式,创建now和last_update这两个类型。


3

创建解析函数

参照SDK中的例程文件user_webserver.c(篇幅原因,去掉一些代码):


LOCAL int ICACHE_FLASH_ATTRlight_status_set(struct jsontree_context *js_ctx, struct jsonparse_state *parser){    while ((type = jsonparse_next(parser)) != 0) {        if (type == JSON_TYPE_PAIR_NAME) {            if (jsonparse_strcmp_value(parser, "red") == 0) {                uint32 status;                jsonparse_next(parser);                jsonparse_next(parser);                status = jsonparse_get_value_as_int(parser);                r=status;                os_printf("R: %d ",status);            } else if (jsonparse_strcmp_value(parser, "green") == 0) {                uint32 status;                jsonparse_next(parser);                jsonparse_next(parser);                status = jsonparse_get_value_as_int(parser);                g=status;                os_printf("G: %d ",status);            }...................        }    }    return 0;}


第2步的函数更新数据,那么数据从哪来?就是这里函数提供的。

程序中会分别判断red对应的值和green对应的值。如果有需要,可以进行相应的操作。不难理解吧?修改一下,换成我们需要的函数(篇幅有限,依然省略部分):


int ICACHE_FLASH_ATTRjson_location_parse(struct jsontree_context *js_ctx, struct jsonparse_state *parser){int type;char buffer[64];while ((type = jsonparse_next(parser)) != 0){if (type == JSON_TYPE_PAIR_NAME){if (jsonparse_strcmp_value(parser, "id") == 0){jsonparse_next(parser);jsonparse_next(parser);jsonparse_copy_value(parser, buffer, sizeof(buffer));os_printf("id is = %s", buffer);}else if(jsonparse_strcmp_value(parser, "name") == 0){jsonparse_next(parser);jsonparse_next(parser);jsonparse_copy_value(parser, buffer, sizeof(buffer));os_printf("name is = %s", buffer);}else if(jsonparse_strcmp_value(parser, "country") == 0){jsonparse_next(parser);jsonparse_next(parser);jsonparse_copy_value(parser, buffer, sizeof(buffer));os_printf("country is = %s", buffer);}.............................}}return 0;}


因为咱们获取的是字符串信息,所以这里使用了函数“jsonparse_copy_value”。同理,创建now和last_updata的解析函数。


4

注册更新JSON数据和解析JSON数据的回调函数

参照SDK中的例程文件user_webserver.c中的代码,第394行:


LOCAL struct jsontree_callback light_callback =JSONTREE_CALLBACK(light_status_get, light_status_set);


light_status_get是生成,light_status_set是解析。回调函数以结构体的形式注册,light_callback是结构体的名字。

所以,参照这个格式往里套,location的代码如下:


LOCAL struct jsontree_callback json_location_Callback =JSONTREE_CALLBACK(json_location_get, json_location_parse);


now和last_updata同理:


LOCAL struct jsontree_callback json_now_Callback =JSONTREE_CALLBACK(json_now_get,json_now_parse);LOCAL struct jsontree_callback json_last_update_Callback =JSONTREE_CALLBACK(json_last_update_get, json_last_update_parse);


5

生成JSON数的对象

参照SDK中的例程文件user_webserver.c中的代码,第397行:


JSONTREE_OBJECT(rgb_tree,                JSONTREE_PAIR("red", &light_callback),                JSONTREE_PAIR("green", &light_callback),                JSONTREE_PAIR("blue", &light_callback),                JSONTREE_PAIR("cwhite", &light_callback),                JSONTREE_PAIR("wwhite", &light_callback),                );


所以,先生成一个location的对象:


JSONTREE_OBJECT(location,                JSONTREE_PAIR("id", &json_location_Callback),                JSONTREE_PAIR("name", &json_location_Callback),                JSONTREE_PAIR("country", &json_location_Callback),                JSONTREE_PAIR("path", &json_location_Callback),                JSONTREE_PAIR("timezone", &json_location_Callback),                JSONTREE_PAIR("timezone_offset", &json_location_Callback));


然后,依次生成now和last_updata这两个对象。

现在分别生成了location、now和last_updata这三个结构,要把这三个结构组合起来:


JSONTREE_OBJECT(resultsArray,JSONTREE_PAIR("location",&location),JSONTREE_PAIR("now", &now),JSONTREE_PAIR("last_update", &json_last_update_Callback));


得到的是这样一个结构:


{"location": {"id": "WX4BZD6KEJFY","name": "Langfang","country": "CN","path": "Langfang,Langfang,Hebei,China","timezone": "Asia/Shanghai","timezone_offset": "+08:00"},"now": {"text": "Overcast","code": "9","temperature": "20"},"last_update": "2019-09-19T20:15:00+08:00"}


然后,加上方括号:

JSONTREE_ARRAY(jsonArray,JSONTREE_PAIR_ARRAY(&resultsArray));


再然后,生成名为“results”的对象:


JSONTREE_OBJECT(object,JSONTREE_PAIR("results", &jsonArray),);


至此,一个完整的数据结构已经创建好了,跟天气网站发给我们的结构是一样的。

再再然后,我们需要把这个结构放进一个对象里,方便调用:


JSONTREE_OBJECT(temp,JSONTREE_PAIR("object", &object),);



6

解析天气数据

参照SDK中的例程文件user_webserver.c中的代码,第1606行:


jsontree_setup(&js, (struct jsontree_value *)&wifi_req_tree, json_putchar);json_parse(&js, pParseBuffer);


改为我们的代码:


struct jsontree_context js;jsontree_setup(&js, (struct jsontree_value *)&object, json_putchar);json_parse(&js,c);


函数“jsontree_setup”生成格式数据树,函数“json_parse”对数据进行解析,并打印出来。其中,“c”是指针,指向从“{”开始的JSON数据。具体可以看我工程里的代码,这里就不再贴出来了。


程序修改完成,保存、清理、编译、下载一条龙,然后重新上电。这里借助串口助手来查看效果。效果如下所示:


从截图上可以看到,程序通过解析函数,已经成功获取到了JSON结构中的所有信息,接下来,你就可以把天气信息显示在某些设备上了。


ESP8266_前言

ESP8266_01搭建开发环境

ESP8266_02程序的编译与下载

ESP8266_03 SDK与Makefile的基本用法

ESP8266_04管脚控制与软件定时器

ESP8266_05 ESP8266有几个串口?

ESP8266_06硬件定时器与IO中断

ESP8266_07基于PWM的呼吸灯

ESP8266_08基于flash的数据掉电保护

ESP8266_09基于IIC控制的OLED屏幕

ESP8266_10 ESP8266的STATION模式

ESP8266_11 ESP8266的UDP广播

ESP8266_12 ESP8266客户端模式下的TCP通信

ESP8266_13服务器端模式下的TCP通信

ESP8266_14 SOFTAP模式下的服务器端和客户端

ESP8266_15天气预报之TCP的GET操作

声明:本文为OFweek维科号作者发布,不代表OFweek维科号立场。如有侵权或其他问题,请及时联系我们举报。
2
评论

评论

    相关阅读

    暂无数据

    单片机爱好者

    51单片机逆向学习实战教程 一书...

    举报文章问题

    ×
    • 营销广告
    • 重复、旧闻
    • 格式问题
    • 低俗
    • 标题夸张
    • 与事实不符
    • 疑似抄袭
    • 我有话要说
    确定 取消

    举报评论问题

    ×
    • 淫秽色情
    • 营销广告
    • 恶意攻击谩骂
    • 我要吐槽
    确定 取消

    用户登录×

    请输入用户名/手机/邮箱

    请输入密码