-
什么是json?
懵逼回答:【参考菜鸟教程】
a.JSON 指的是 JavaScript 对象表示法(JavaScript Object Notation)
b.JSON 是轻量级的文本数据交换格式
c.JSON 独立于语言:JSON 使用 Javascript语法来描述数据对象,但是 JSON 仍然独立于语言和平台。JSON 解析器和 JSON 库支持许多不同的编程语言。 目前非常多的动态(PHP,JSP,.NET)编程语言都支持JSON。
d.JSON 具有自我描述性,更易理解。
理解:
说到底json的作用就是为了解决不同平台下的字节对齐、类型大小不统一的问题,把数据封装成具有一定格式的字符流数据,进行传输。
-
JSON 语法规则【具体规则请参考菜鸟教程】
JSON 数据的书写格式是:名称/值对。
名称/值对包括字段名称(在双引号中),后面写一个冒号,然后是值。名称/值对中的值可以是: 数字(整数或浮点数) 字符串(在双引号中) 逻辑值(true 或 false) 数组(在中括号中) 对象(在大括号中) null
JSON 数据具体化:
a.JSON 对象以大括号“{}”表示,对象可以包含多个 key/value(键/值)对。key 必须是字符串,value 可以是合法的 JSON 数据类型(字符串, 数字, 对象, 数组, 布尔值或 null)。key 和 value 中使用冒号(:)分割,每个 key/value 对使用逗号(,)分割。b. JSON 数组以中括号“[]”表示,数组可包含多个对象;
-
心知天气JSON 数据分析
>访问心知天气的api,返回json数据。https://api.seniverse.com/v3/weather/now.json?key=smtq3n0ixdggurox&location=beijing&language=zh-Hans&unit=c
>返回的json数据
{"results":[{"location":{"id":"WX4FBXXFKE4F","name":"北京","country":"CN","path":"北京,北京,中国","timezone":"Asia/Shanghai","timezone_offset":"+08:00"},"now":{"text":"晴","code":"0","temperature":"28"},"last_update":"2019-10-02T13:25:00+08:00"}]}
>使用菜鸟工具里的json格式化工具格式化json数据 【参考菜鸟json格式化工具】
{ "results": [ { "location": { "id": "WX4FBXXFKE4F", "name": "北京", "country": "CN", "path": "北京,北京,中国", "timezone": "Asia/Shanghai", "timezone_offset": "+08:00" }, "now": { "text": "晴", "code": "0", "temperature": "28" }, "last_update": "2019-10-02T13:25:00+08:00" } ] }
>json数据分析
a."text": "晴" 分析:text表示键,晴表示值,这个值的类型是字符串(在双引号中),键和值构成了一对(PAIR),故名键/值对。 b. { <---1 "location": {<---2 "id": "WX4FBXXFKE4F", "name": "北京", "country": "CN", "path": "北京,北京,中国", "timezone": "Asia/Shanghai", "timezone_offset": "+08:00" }, "now": { "text": "晴", "code": "0", "temperature": "28" }, "last_update": "2019-10-02T13:25:00+08:00" } >1指向的大括号表示一个json对象,json对象里面有三个键/值对(key分别为"location"、"now"、"last_update"), 前两个键值对的值类型是对象(在大括号中),最后一个键值对的值类型是字符串(在双引号中)。 >2指向的是一个键/值对,键/值对的值类型为对象(在大括号中),这个对象里面有六个键/值对, 这六个键/值对的值类型都是字符串(在双引号中)。 可以看到心知天气的json参数值都是字符串类型。 c. { <---1 "results": [ <---2 { ...... } ] } >1指向的大括号表示一个json对象,json对象里面有一个键/值对,键/值对的值类型为数组(在中括号中)。 >2指向的是一个键/值对,键/值对的值类型为数组(在中括号中),JSON 数组中有一个json对象
>到这里,json的说明及心知天气json数据分析已经完成,下面进入乐鑫json解析源码分析阶段
-
乐鑫json解析源码分析
>为了尝试勾起博友们的欲望,先把结果贴出来>心知天气json树生成结果。是不是一模一样,值部分没做处理<_< { "result": [ { "location": { "id": "", "name": "", "country": "", "path": "", "timezone": "", "timezone_offset": "" }, "now": { "text": "", "code": "", "temperature": "" }, "last_update": "" } ], //前面没有分析返回错误状态码,这里为了出错处理,也把下面这两个键/值对考虑进去了,具体去看官方的说明,下面提供链接。 "status": "", "status_code": "" } >心知天气json树解析结果 a.返回正确json解析的结果(我这边使用的是安信可得串口调试助手,然后输出中文会乱码, 所以让服务器返回的语言为英文,具体参考官方文档说明,下面给出了文档链接): WX4FBXXFKE4F Beijing CN Beijing,Beijing,China Asia/Shanghai +08:00 Sunny 0 28 2019-10-02T16:50:00+08:00 b.返回错误原因json解析的结果: Wrong parameters. AP010001
【–>API 返回错误代码说明】
【–>接口中的通用参数】
【–>免费用户可以调用哪些数据】>源码的分析采用“从源头到结果”分析方法
源码路径: ESP8266_NONOS_SDK-2.2.1>examples>IoT_Demo>user> user_webserver.c user_json.c 主要就是这两个源文件,既然要从源头分析json的解析, 那么这个源头应该就是在成功收到服务器发来数据后开始解析的那一段代码开始的。
>>分析user_webserver.c文件,代码阅读请从下往上看最佳<
//15.json解析实现,这里只列出wifi_station_set,wifi_softap_set与wifi_station_set类似 /****************************************************************************** * FunctionName : wifi_station_set * Description : 将一个json格式解析成station的参数 * Parameters : js_ctx -- 一个指向JSON设置的指针 * parser -- 指向JSON解析器状态的指针 * Returns : result *******************************************************************************/ LOCAL int ICACHE_FLASH_ATTR wifi_station_set(struct jsontree_context *js_ctx, struct jsonparse_state *parser) { int type; uint8 station_tree; //16.解析 JSON 格式下⼀个元素 //函数jsonparse_next()的返回值表示的该元素的类型 //这些类型已在 json.h文件中定义,如下: /** * #define JSON_TYPE_ARRAY '[' <--表示json数组类型 * #define JSON_TYPE_OBJECT '{' <--表示json对象类型 * #define JSON_TYPE_PAIR ':' <---名称与值之间的冒号 * #define JSON_TYPE_PAIR_NAME 'N' <--json数据的名称类型 * * #define JSON_TYPE_STRING '"' <-- 字符串数据类型 * #define JSON_TYPE_INT 'I' <-- 整型数据类型 * #define JSON_TYPE_NUMBER '0' * #define JSON_TYPE_ERROR 0 * * #define JSON_TYPE_NULL 'n' * #define JSON_TYPE_TRUE 't' <--布尔数据,true * #define JSON_TYPE_FALSE 'f' <--<--布尔数据,true * * #define JSON_TYPE_CALLBACK 'C' <--回调函数类型 */ while ((type = jsonparse_next(parser)) != 0) { //18.找到名称,名称可以是"Station"、"Softap"、"ssid"...等 if (type == JSON_TYPE_PAIR_NAME) { char buffer[64]; os_bzero(buffer, 64); //可以添加下面两行验证,打印的结果为:Request->Station->Connect_Station->ssid...(这里需要提醒一下,json数据本身就是字符串,它会按字符串的顺序进行解析) // jsonparse_copy_value(parser, buffer, sizeof(buffer)); //os_printf("---->%s\n",buffer); //19.比较解析 JSON 数据与特定字符串 //比较成功说明已解析到了"Station"部分 /** * "Station": { * "Connect_Station": { * "ssid": ?, * "password": ?, * "token": ?, * } */ if (jsonparse_strcmp_value(parser, "Station") == 0) { //20.将station_tree标志设置为1,表示在未到 Softap之前的数据都在条件if (station_tree) {}里解析 station_tree = 1; } else if (jsonparse_strcmp_value(parser, "Softap") == 0) { //21.同21 station_tree = 0; } if (station_tree) { if (jsonparse_strcmp_value(parser, "ssid") == 0) { jsonparse_next(parser);//跳过 : jsonparse_next(parser);//跳过 " //到达值部分,这里可以看到值类型都是字符串,乐鑫官方文档还列出了其他不同数据类型获取的接口,如: //jsonparse_get_value_as_int--解析JSON 格式为整型数据 //jsonparse_get_value_as_long--解析 JSON 格式为为⻓整型数据 //还有其它判断值类型的接口,更多请查看《ESP8266 Non-OS SDK API 参考》 //复制当前解析字符串到指定缓存 jsonparse_copy_value(parser, buffer, sizeof(buffer)); os_memcpy(sta_conf->ssid, buffer, os_strlen(buffer)); } else if (jsonparse_strcmp_value(parser, "password") == 0) { jsonparse_next(parser); jsonparse_next(parser); jsonparse_copy_value(parser, buffer, sizeof(buffer)); os_memcpy(sta_conf->password, buffer, os_strlen(buffer)); } else if (jsonparse_strcmp_value(parser, "token") == 0) { jsonparse_next(parser); jsonparse_next(parser); jsonparse_copy_value(parser, buffer, sizeof(buffer)); user_esp_platform_set_token(buffer); } } } } return 0; } //14.同12 LOCAL struct jsontree_callback wifi_station_callback = JSONTREE_CALLBACK(NULL, wifi_station_set); //13.同11 //既 { "wifi": { "Request": { "Station": { "Connect_Station": { "ssid": ?, "password": ?, "token": ? } }, "Softap": { "Ipinfo_Softap": { "authmode": ?, "channel": ?, "ssid": ?, "password": ? } } } } } JSONTREE_OBJECT(set_station_config_tree, JSONTREE_PAIR("ssid", &wifi_station_callback), JSONTREE_PAIR("password", &wifi_station_callback), JSONTREE_PAIR("token", &wifi_station_callback)); //12.wifi_softap_callback接口实现 LOCAL struct jsontree_callback wifi_softap_callback = //由用户实现对4个键/值对("authmode"、"channel"、"ssid"、"password")值类型定义赋值和值类型解析 //参数1:实现值类型定义赋值的接口,本博文只解析json数据,不生成json数据,故不说明第一个参数,如果不使用可以赋 NULL //参数2:实现对值解析的接口 JSONTREE_CALLBACK(NULL ,wifi_softap_set); //11.创建4个键/值对,键1为"authmode",键2为...,值的类型未知,由wifi_softap_callback决定, //这里注册了一个wifi_softap_callback函数,用于用户的数据处理。 //既 { "wifi": { "Request": { "Station": { "Connect_Station": ? }, "Softap": { "Ipinfo_Softap": { "authmode": ?, "channel": ?, "ssid": ?, "password": ? } } } } } JSONTREE_OBJECT(softap_config_tree, JSONTREE_PAIR("authmode", &wifi_softap_callback), JSONTREE_PAIR("channel", &wifi_softap_callback), JSONTREE_PAIR("ssid", &wifi_softap_callback), JSONTREE_PAIR("password", &wifi_softap_callback)); //10.生成一个键/值对,键为"Connect_Station",值的类型为对象(JSONTREE_OBJECT) //既 { "wifi": { "Request": { "Station": { "Connect_Station": ? }, "Softap": { "Ipinfo_Softap": ? } } } } JSONTREE_OBJECT(set_station_tree, JSONTREE_PAIR("Connect_Station", &set_station_config_tree)); //9.生成一个键/值对,键为"Ipinfo_Softap",值的类型为对象(JSONTREE_OBJECT) //既 { "wifi": { "Request": { "Station": ?, "Softap": { "Ipinfo_Softap": ? } } } } JSONTREE_OBJECT(set_softap_tree, JSONTREE_PAIR("Ipinfo_Softap", &softap_config_tree)); //8.生成两个键/值对,键1为"Station",键2为"Softap",值的类型都为对象(JSONTREE_OBJECT) //既 { "wifi": { "Request": { "Station": ?, "Softap": ? } } } JSONTREE_OBJECT(set_wifi_tree, JSONTREE_PAIR("Station", &set_station_tree), JSONTREE_PAIR("Softap", &set_softap_tree)); //7.生成一个键/值对,键为"Request",值的类型为对象(JSONTREE_OBJECT) //既 { "wifi": { "Request": ? } } JSONTREE_OBJECT(wifi_request_tree, JSONTREE_PAIR("Request", &set_wifi_tree)); //6.生成一个键/值对,键为"wifi",值的类型为对象(JSONTREE_OBJECT) //既 {"wifi": ?}---->值由wifi_request_tree的定义的json类型确定,这里的wifi_request_tree由上面可知是一个json对象,当为了理解,这里将值类型表示为? JSONTREE_OBJECT(wifi_req_tree, JSONTREE_PAIR("wifi", &wifi_request_tree)); /****************************************************************************** * FunctionName : webserver_recv * 描述 : 处理来自服务器的接收数据 * 参数 : arg -- 传递给回调函数的附加参数 * pusrdata -- 接收到的数据(连接已关闭时为NULL!) * length -- 接收数据的长度 * 返回值 : 无 *******************************************************************************/ //2.实现成功接收网络数据的回调函数 LOCAL void ICACHE_FLASH_ATTR webserver_recv(void *arg, char *pusrdata, unsigned short length) { ...... os_printf("len:%u\n",length); //3.判断数据的有效性 //原理就是从tcp协议头中的Content-Length: 256取出内容的长度256bytes,然后再去计算json数据的长度, 两者进行比较。 //数据正确返回true,错误返回false,该函数可以用到我们实际的项目中,用于出错处理 if(check_data(pusrdata, length) == false) { //出错处理 } ...... /*parse_url(precvbuffer, pURL_Frame);*/ //parse_url函数用于url解析,我们这里不做服务器,只是从服务器接收数据,这边只是列出来,使我们在有需要的时候去使用它。 ....... //再往下看,就到我们重点关注的地方了 struct jsontree_context js; //os_strstr(str1,str2) 函数用于判断字符串str2是否是str1的子串。如果是,则该函数返回 str1字符串从 str2第一次出现的位置开始到 str1结尾的字符串;否则,返回NULL。 //pParseBuffer=(char *)os_strstr(pusrdata, "{");//接受数据处理协议头举例 //4.JSON 解析初始化 //参数1:JSON 解析指针 //参数2:JSON 解析字符串 -- 用户自定义的json字符串 //参数3:字符串⻓度 --- 已在user_json.c文件中实现,直接使用即可 jsontree_setup(&js, (struct jsontree_value *)&wifi_req_tree, json_putchar); //5.将数据解析为JSON格式 //参数1:JSON 解析指针 //参数2:从服务器接收到的json数据,需要去掉协议头部分。 json_parse(&js, pParseBuffer); } //1.注册成功接收网络数据的回调函数 espconn_regist_recvcb(pesp_conn, webserver_recv);
>>总体流程图
>>将我们自定义json数据格式化成json树格式,然后保存到pbuf中
该函数也是在user_webserver.c使用中,为了体现功能的不同这里把它拿出来单独说明,函数是在user_json.c中定义的。//设置字符串的JSON格式树 //参数1:指向JSON格式树的指针--用户自定义的json数据 //参数2:指向JSON格式树路径的指针 //参数3:用于保存格式化后的json数据的指针 json_ws_send((struct jsontree_value *)&wifi_info_tree, "wifi", pbuf); ------------------------------------------------------------------------------------------------- >>为了验证结构,将上文中创建的json树打印出来 char pbuf[1024]; 1. json_ws_send((struct jsontree_value *)&wifi_info_tree, "wifi", pbuf); os_printf("%s",pbuf); 打印结果: { "Request": { "Station": { "Connect_Station": { "ssid": "", "password": "", "token": "" } }, "Softap": { "Ipinfo_Softap": { "authmode": "", "channel": "", "ssid": "", "password": "" } } } } 由结果可以清晰的知道json_ws_send((struct jsontree_value *)&wifi_info_tree, "wifi", pbuf)函数 是把与"wifi"对应的键/值对的值(json对象)格式化成json字符串保存到了pbuf中。 2.把整个用户自定义的json数据打印出来 json_ws_send((struct jsontree_value *)&wifi_info_tree, "{", pbuf); os_printf("%s",pbuf); 打印结果: { "wifi": { "Request": { "Station": { "Connect_Station": { "ssid": "", "password": "", "token": "" } }, "Softap": { "Ipinfo_Softap": { "authmode": "", "channel": "", "ssid": "", "password": "" } } } } }
由于没有实现生成json数据的接口,该接口为NULL,所以值默认都是为""。
>>分析宏JSONTREE_OBJECT与JSONTREE_PAIR、JSONTREE_ARRAY与JSONTREE_PAIR_ARRAY:知识点: 1.为了防止当宏定义过长影响阅读,可以通过在行中添加反斜杠“\”,让过长的"一行"拆分成几行 2.省略号"…"代表可变的部分,__VA_AEGS__部分会替换成带参宏"..."部分的内容 3.##用于与带参宏结合,与别的字段进行连接的格式化宏命令 JSONTREE_OBJECT宏: #define JSONTREE_OBJECT(name, ...) \ static struct jsontree_pair jsontree_pair_##name[] = {__VA_ARGS__}; \ static struct jsontree_object name = { \ JSON_TYPE_OBJECT, \ sizeof(jsontree_pair_##name)/sizeof(struct jsontree_pair), \ jsontree_pair_##name } JSONTREE_PAIR宏: #define JSONTREE_PAIR(name, value) {(name), (struct jsontree_value *)(value)} 宏具体化: JSONTREE_OBJECT(wifi_req_tree, JSONTREE_PAIR("wifi", &wifi_request_tree)); 将宏展开: a/创建了一个键/值对,键为"wifi",值的类型由wifi_request_tree决定 static struct jsontree_pair jsontree_pair_wifi_req_tree[] = { {"wifi", &wifi_request_tree} }; b/创建了一个json对象,该对象里面有一个键/值对 static struct jsontree_object wifi_req_tree = {JSON_TYPE_OBJECT, sizeof(jsontree_pair_wifi_req_tree)/sizeof(struct jsontree_pair),//计算键值对的数目 jsontree_pair_wifi_req_tree//将键/值对注册到该json对象中 } 形象化结果: {"wifi" : ?} 如果是两个键/值对,如: JSONTREE_OBJECT(set_wifi_tree, JSONTREE_PAIR("Station", &set_station_tree), JSONTREE_PAIR("Softap", &set_softap_tree)); 将宏展开: a/ static struct jsontree_pair jsontree_pair_set_wifi_tree[] = { {"Station", &set_station_tree}, {"Softap", &set_softap_tree} }; b/ static struct jsontree_object set_wifi_tree = {JSON_TYPE_OBJECT, sizeof(jsontree_pair_set_wifi_tree)/sizeof(struct jsontree_pair), jsontree_pair_set_wifi_tree/ } 形象化结果: {"Station" : ?,"Softap" : ?} JSONTREE_ARRAY宏: #define JSONTREE_ARRAY(name, ...) \ static struct jsontree_value* jsontree_value_##name[] = {__VA_ARGS__}; \ static struct jsontree_array name = { \ JSON_TYPE_ARRAY, \ sizeof(jsontree_value_##name)/sizeof(struct jsontree_value*), \ jsontree_value_##name } JSONTREE_PAIR_ARRAY宏: #define JSONTREE_PAIR_ARRAY(value) (struct jsontree_value *)(value) //这里先说明,官方也使用了这两个宏,然后心知天气的json数据也用到了json数组,所以这里以心知天气的json数据举例。 JSONTREE_ARRAY(seniverse_req_tree, JSONTREE_PAIR_ARRAY(&seniverse_info_tree), ); 将宏展开: a/创建一个json对象 static struct jsontree_value* jsontree_value_seniverse_req_tree[] = {&seniverse_info_tree}; b/创建一个json数组,数组里有一个json对象 static struct jsontree_array seniverse_req_tree = {JSON_TYPE_ARRAY, sizeof(jsontree_value_seniverse_req_tree)/sizeof(struct jsontree_value*), //计算json对象的数目 jsontree_value_seniverse_req_tree //注册json对象到json数组中 } 形象化结果: [ { } ]
>>到这,乐鑫官方源码例程的分析结束,相信博友们以懂得了如何去使用这下接口,下面进入心知天气json字符串的解析阶段
-
心知天气json字符串的解析实现
>>开头已经分析了心知天气的json字符串,这里就不再分析,直接把json数据生成的过程展示出来/****************************************************************************** * FunctionName : seniverse_info_set * Description : 将一个json格式解析成心知天气的参数 * Parameters : js_ctx -- 一个指向JSON设置的指针 * parser -- 指向JSON解析器状态的指针 * Returns : result *******************************************************************************/ LOCAL int ICACHE_FLASH_ATTR seniverse_info_set(struct jsontree_context *js_ctx, struct jsonparse_state *parser) { int type; uint8 seniverse_tree=0; //解析 JSON 格式下⼀个元素 while ((type = jsonparse_next(parser)) != 0){ if (type == JSON_TYPE_PAIR_NAME) { char buffer[64]; os_bzero(buffer, 64); //比较解析 JSON 数据与特定字符串 if(jsonparse_strcmp_value(parser, "location") == 0){ seniverse_tree = 1; }else if (jsonparse_strcmp_value(parser, "now") == 0){ seniverse_tree = 2; }else if (jsonparse_strcmp_value(parser, "last_update") == 0){ jsonparse_next(parser); jsonparse_next(parser); jsonparse_copy_value(parser, buffer, sizeof(buffer)); os_printf("%s\n",buffer); }else if (jsonparse_strcmp_value(parser, "status") == 0){ jsonparse_next(parser); jsonparse_next(parser); jsonparse_copy_value(parser, buffer, sizeof(buffer)); os_printf("%s\n",buffer); }else if (jsonparse_strcmp_value(parser, "status_code") == 0){ jsonparse_next(parser); jsonparse_next(parser); jsonparse_copy_value(parser, buffer, sizeof(buffer)); os_printf("%s\n",buffer); } if(seniverse_tree == 1){ if (jsonparse_strcmp_value(parser, "id") == 0){ //解析 JSON 格式下⼀个元素 jsonparse_next(parser); jsonparse_next(parser); jsonparse_copy_value(parser, buffer, sizeof(buffer)); os_printf("%s\n",buffer); }else if (jsonparse_strcmp_value(parser, "name") == 0){ jsonparse_next(parser); jsonparse_next(parser); jsonparse_copy_value(parser, buffer, sizeof(buffer)); os_printf("%s\n",buffer); }else if (jsonparse_strcmp_value(parser, "country") == 0){ jsonparse_next(parser); jsonparse_next(parser); jsonparse_copy_value(parser, buffer, sizeof(buffer)); os_printf("%s\n",buffer); }else if (jsonparse_strcmp_value(parser, "path") == 0){ jsonparse_next(parser); jsonparse_next(parser); jsonparse_copy_value(parser, buffer, sizeof(buffer)); os_printf("%s\n",buffer); }else if (jsonparse_strcmp_value(parser, "timezone") == 0){ jsonparse_next(parser); jsonparse_next(parser); jsonparse_copy_value(parser, buffer, sizeof(buffer)); os_printf("%s\n",buffer); }else if (jsonparse_strcmp_value(parser, "timezone_offset") == 0){ jsonparse_next(parser); jsonparse_next(parser); jsonparse_copy_value(parser, buffer, sizeof(buffer)); os_printf("%s\n",buffer); } }else if(seniverse_tree == 2){ if (jsonparse_strcmp_value(parser, "text") == 0){ //解析 JSON 格式下⼀个元素 jsonparse_next(parser); jsonparse_next(parser); jsonparse_copy_value(parser, buffer, sizeof(buffer)); os_printf("%s\n",buffer); }else if (jsonparse_strcmp_value(parser, "code") == 0){ jsonparse_next(parser); jsonparse_next(parser); jsonparse_copy_value(parser, buffer, sizeof(buffer)); os_printf("%s\n",buffer); }else if (jsonparse_strcmp_value(parser, "temperature") == 0){ jsonparse_next(parser); jsonparse_next(parser); jsonparse_copy_value(parser, buffer, sizeof(buffer)); os_printf("%s\n",buffer); } } } } return 0; } LOCAL struct jsontree_callback seniverse_handle_callback = JSONTREE_CALLBACK(NULL, seniverse_info_set); JSONTREE_OBJECT(seniverse_location_tree, JSONTREE_PAIR("id", &seniverse_handle_callback), JSONTREE_PAIR("name", &seniverse_handle_callback), JSONTREE_PAIR("country", &seniverse_handle_callback), JSONTREE_PAIR("path", &seniverse_handle_callback), JSONTREE_PAIR("timezone", &seniverse_handle_callback), JSONTREE_PAIR("timezone_offset", &seniverse_handle_callback) ); JSONTREE_OBJECT(seniverse_now_tree, JSONTREE_PAIR("text", &seniverse_handle_callback), JSONTREE_PAIR("code", &seniverse_handle_callback), JSONTREE_PAIR("temperature", &seniverse_handle_callback) ); JSONTREE_OBJECT(seniverse_info_tree, JSONTREE_PAIR("location", &seniverse_location_tree), JSONTREE_PAIR("now", &seniverse_now_tree), JSONTREE_PAIR("last_update", &seniverse_handle_callback) ); JSONTREE_ARRAY(seniverse_req_tree, JSONTREE_PAIR_ARRAY(&seniverse_info_tree), ); JSONTREE_OBJECT(seniverse_object_tree, JSONTREE_PAIR("result", &seniverse_req_tree), JSONTREE_PAIR("status", &seniverse_handle_callback), JSONTREE_PAIR("status_code", &seniverse_handle_callback) ); /****************************************************************************** * FunctionName : seniverse_recv_cb * Description : 处理从心知天气接收的数据 * Parameters : arg -- 传递给回调函数的附加参数 * pusrdata -- 接收到的数据(或连接已关闭时为NULL)! * length -- 接收数据的长度 * Returns : none *******************************************************************************/ //成功接收网络数据的回调函数 LOCAL void ICACHE_FLASH_ATTR seniverse_recv_cb(void *arg, char *pdata, unsigned short length){ struct espconn *pespconn = arg; char *pParse_json=NULL; os_printf("len:%u\n",length); if(check_data(pdata, length) == false)//判断接收到的数据长度 { os_printf("seniverse_recv_err\n");//数据接收错误 /* * 在这里实现向服务器重发get请求 * */ return; } // os_printf("%s",pdata); //找到json字符串 pParse_json=(char *)os_strstr(pdata, "\r\n\r\n{"); if(NULL == pParse_json) { //出错处理 } pParse_json += 4;//指向"{" struct jsontree_context js; //生成 JSON格式数据树 jsontree_setup(&js, (struct jsontree_value *)&seniverse_object_tree, json_putchar); //char pbuf[1024]; //json_ws_send((struct jsontree_value *)&seniverse_object_tree, "{", pbuf); //os_printf("%s",pbuf); json_parse(&js, pParse_json); }
>>串口打印解析结果
-
心知天气补充
>>前面只介绍的和解析的是天气类的天气实况的api(获取指定城市的天气实况。付费用户可获取全部数据,免费用户只返回天气现象文字、代码和气温 3 项数据。注:中国城市暂不支持云量和露点温度。)
还有其他api,比如:
a/逐日天气预报和昨日天气api:获取指定城市未来最多 15 天每天的白天和夜间预报,以及昨日的历史天气。付费用户可获取全部数据,免费用户只返回 3 天天气预报。
b/过去24小时历史天气api获取指定城市过去24小时逐小时的历史天气。
c/等等…
更多请查看心知天气文档–>【天气类】
>>为了功能的多样性,这里再增加<<逐日天气预报和昨日天气api>>的json字符串解析逐日天气预报和昨日天气api,由于实现方式大同小异,这里只获取今天和明天的json数据, 需要提醒一下,本人使用的是免费的接口,个人使用,没钱包年包月 ( •᷄⌓•᷅ )੨੨南上加南。 https://api.seniverse.com/v3/weather/daily.json?key=smtq3n0ixdggurox&location=beijing&language=zh-Hans&unit=c&days=2 访问上面的api返回的json数据: { "results": [ { "location": { "id": "WX4FBXXFKE4F", "name": "北京", "country": "CN", "path": "北京,北京,中国", "timezone": "Asia/Shanghai", "timezone_offset": "+08:00" }, "daily": [ //返回指定days天数的结果 { "date": "2019-10-03",//日期 "text_day": "多云",//白天天气现象文字 "code_day": "4", //白天天气现象代码 "text_night": "小雨",//晚间天气现象文字 "code_night": "13",//晚间天气现象代码 "high": "29", //当天最高温度 "low": "13",//当天最低温度 "precip": "", //降水概率,范围0~100,单位百分比(目前仅支持国外城市) "wind_direction": "东南",//风向文字 "wind_direction_degree": "135",//风向角度,范围0~360 "wind_speed": "25.20",//风速,单位km/h(当unit=c时)、mph(当unit=f时) "wind_scale": "4" //风力等级 }, { "date": "2019-10-04", "text_day": "小雨", "code_day": "13", "text_night": "晴", "code_night": "1", "high": "20", "low": "9", "precip": "", "wind_direction": "东北", "wind_direction_degree": "45", "wind_speed": "25.20", "wind_scale": "4" } ], "last_update": "2019-10-03T11:17:49+08:00" } ] }
>>同样,先把生成的json树和json字符串解析结果展示出来
1.生成的json树: { "result": [ { "location": { "id": "", "name": "", "country": "", "path": "", "timezone": "", "timezone_offset": "" }, "daily": [ { "date": "", "text_day": "", "code_day": "", "text_night": "", "code_night": "", "high": "", "low": "", "precip": "", "wind_direction": "", "wind_direction_degree": "", "wind_speed": "", "wind_scale": "" }, { "date": "", "text_day": "", "code_day": "", "text_night": "", "code_night": "", "high": "", "low": "", "precip": "", "wind_direction": "", "wind_direction_degree": "", "wind_speed": "", "wind_scale": "" } ], "last_update": "" } ], "status": "", "status_code": "" } 2.json字符串解析结果:(同样,这里设置返回的json数据语言为英) WX4FBXXFKE4F Beijing CN Beijing,Beijing,China Asia/Shanghai +08:00 2019-10-03 Cloudy 4 Light rain 13 29 13 SE 135 25.20 4 2019-10-04 Light rain 13 Clear 1 20 9 NE 45 25.20 4 2019-10-03T11:17:49+08:00
>>下面直接把代码展示出来,这里不再多做说明了,获取到的具体数据处理自行实现
/****************************************************************************** * FunctionName : seniverse_info_set * Description : 将一个json格式解析成心知天气的参数 * Parameters : js_ctx -- 一个指向JSON设置的指针 * parser -- 指向JSON解析器状态的指针 * Returns : result *******************************************************************************/ LOCAL int ICACHE_FLASH_ATTR seniverse_info_set(struct jsontree_context *js_ctx, struct jsonparse_state *parser) { int type; uint8 seniverse_tree=0; //解析 JSON 格式下⼀个元素 while ((type = jsonparse_next(parser)) != 0){ if (type == JSON_TYPE_PAIR_NAME) { char buffer[64]; os_bzero(buffer, 64); //比较解析 JSON 数据与特定字符串 if(jsonparse_strcmp_value(parser, "location") == 0){ seniverse_tree = 1; }else if (jsonparse_strcmp_value(parser, "daily") == 0){ seniverse_tree = 2; }else if (jsonparse_strcmp_value(parser, "last_update") == 0){ jsonparse_next(parser); jsonparse_next(parser); jsonparse_copy_value(parser, buffer, sizeof(buffer)); os_printf("%s\n",buffer); }else if (jsonparse_strcmp_value(parser, "status") == 0){ jsonparse_next(parser); jsonparse_next(parser); jsonparse_copy_value(parser, buffer, sizeof(buffer)); os_printf("%s\n",buffer); }else if (jsonparse_strcmp_value(parser, "status_code") == 0){ jsonparse_next(parser); jsonparse_next(parser); jsonparse_copy_value(parser, buffer, sizeof(buffer)); os_printf("%s\n",buffer); } if(seniverse_tree == 1){ if (jsonparse_strcmp_value(parser, "id") == 0){ //解析 JSON 格式下⼀个元素 jsonparse_next(parser); jsonparse_next(parser); jsonparse_copy_value(parser, buffer, sizeof(buffer)); os_printf("%s\n",buffer); }else if (jsonparse_strcmp_value(parser, "name") == 0){ jsonparse_next(parser); jsonparse_next(parser); jsonparse_copy_value(parser, buffer, sizeof(buffer)); os_printf("%s\n",buffer); }else if (jsonparse_strcmp_value(parser, "country") == 0){ jsonparse_next(parser); jsonparse_next(parser); jsonparse_copy_value(parser, buffer, sizeof(buffer)); os_printf("%s\n",buffer); }else if (jsonparse_strcmp_value(parser, "path") == 0){ jsonparse_next(parser); jsonparse_next(parser); jsonparse_copy_value(parser, buffer, sizeof(buffer)); os_printf("%s\n",buffer); }else if (jsonparse_strcmp_value(parser, "timezone") == 0){ jsonparse_next(parser); jsonparse_next(parser); jsonparse_copy_value(parser, buffer, sizeof(buffer)); os_printf("%s\n",buffer); }else if (jsonparse_strcmp_value(parser, "timezone_offset") == 0){ jsonparse_next(parser); jsonparse_next(parser); jsonparse_copy_value(parser, buffer, sizeof(buffer)); os_printf("%s\n",buffer); } }else if(seniverse_tree == 2){ if (jsonparse_strcmp_value(parser, "date") == 0){ //解析 JSON 格式下⼀个元素 jsonparse_next(parser); jsonparse_next(parser); jsonparse_copy_value(parser, buffer, sizeof(buffer)); os_printf("%s\n",buffer); }else if (jsonparse_strcmp_value(parser, "text_day") == 0){ jsonparse_next(parser); jsonparse_next(parser); jsonparse_copy_value(parser, buffer, sizeof(buffer)); os_printf("%s\n",buffer); }else if (jsonparse_strcmp_value(parser, "code_day") == 0){ jsonparse_next(parser); jsonparse_next(parser); jsonparse_copy_value(parser, buffer, sizeof(buffer)); os_printf("%s\n",buffer); }else if (jsonparse_strcmp_value(parser, "text_night") == 0){ jsonparse_next(parser); jsonparse_next(parser); jsonparse_copy_value(parser, buffer, sizeof(buffer)); os_printf("%s\n",buffer); }else if (jsonparse_strcmp_value(parser, "code_night") == 0){ jsonparse_next(parser); jsonparse_next(parser); jsonparse_copy_value(parser, buffer, sizeof(buffer)); os_printf("%s\n",buffer); }else if (jsonparse_strcmp_value(parser, "high") == 0){ jsonparse_next(parser); jsonparse_next(parser); jsonparse_copy_value(parser, buffer, sizeof(buffer)); os_printf("%s\n",buffer); }else if (jsonparse_strcmp_value(parser, "low") == 0){ jsonparse_next(parser); jsonparse_next(parser); jsonparse_copy_value(parser, buffer, sizeof(buffer)); os_printf("%s\n",buffer); }else if (jsonparse_strcmp_value(parser, "precip") == 0){ jsonparse_next(parser); jsonparse_next(parser); jsonparse_copy_value(parser, buffer, sizeof(buffer)); os_printf("%s\n",buffer); }else if (jsonparse_strcmp_value(parser, "wind_direction") == 0){ jsonparse_next(parser); jsonparse_next(parser); jsonparse_copy_value(parser, buffer, sizeof(buffer)); os_printf("%s\n",buffer); }else if (jsonparse_strcmp_value(parser, "wind_direction_degree") == 0){ jsonparse_next(parser); jsonparse_next(parser); jsonparse_copy_value(parser, buffer, sizeof(buffer)); os_printf("%s\n",buffer); }else if (jsonparse_strcmp_value(parser, "wind_speed") == 0){ jsonparse_next(parser); jsonparse_next(parser); jsonparse_copy_value(parser, buffer, sizeof(buffer)); os_printf("%s\n",buffer); }else if (jsonparse_strcmp_value(parser, "wind_scale") == 0){ jsonparse_next(parser); jsonparse_next(parser); jsonparse_copy_value(parser, buffer, sizeof(buffer)); os_printf("%s\n",buffer); } } } } return 0; } LOCAL struct jsontree_callback seniverse_handle_callback = JSONTREE_CALLBACK(NULL, seniverse_info_set); JSONTREE_OBJECT(seniverse_daily_par_tree, JSONTREE_PAIR("date", &seniverse_handle_callback), JSONTREE_PAIR("text_day", &seniverse_handle_callback), JSONTREE_PAIR("code_day", &seniverse_handle_callback), JSONTREE_PAIR("text_night", &seniverse_handle_callback), JSONTREE_PAIR("code_night", &seniverse_handle_callback), JSONTREE_PAIR("high", &seniverse_handle_callback), JSONTREE_PAIR("low", &seniverse_handle_callback), JSONTREE_PAIR("precip", &seniverse_handle_callback), JSONTREE_PAIR("wind_direction", &seniverse_handle_callback), JSONTREE_PAIR("wind_direction_degree", &seniverse_handle_callback), JSONTREE_PAIR("wind_speed", &seniverse_handle_callback), JSONTREE_PAIR("wind_scale", &seniverse_handle_callback) ); JSONTREE_OBJECT(seniverse_location_tree, JSONTREE_PAIR("id", &seniverse_handle_callback), JSONTREE_PAIR("name", &seniverse_handle_callback), JSONTREE_PAIR("country", &seniverse_handle_callback), JSONTREE_PAIR("path", &seniverse_handle_callback), JSONTREE_PAIR("timezone", &seniverse_handle_callback), JSONTREE_PAIR("timezone_offset", &seniverse_handle_callback) ); JSONTREE_ARRAY(seniverse_daily_tree, JSONTREE_PAIR_ARRAY(&seniverse_daily_par_tree), JSONTREE_PAIR_ARRAY(&seniverse_daily_par_tree) ); JSONTREE_OBJECT(seniverse_info_tree, JSONTREE_PAIR("location", &seniverse_location_tree), JSONTREE_PAIR("daily", &seniverse_daily_tree), JSONTREE_PAIR("last_update", &seniverse_handle_callback) ); JSONTREE_ARRAY(seniverse_req_tree, JSONTREE_PAIR_ARRAY(&seniverse_info_tree), ); JSONTREE_OBJECT(seniverse_object_tree, JSONTREE_PAIR("result", &seniverse_req_tree), JSONTREE_PAIR("status", &seniverse_handle_callback), JSONTREE_PAIR("status_code", &seniverse_handle_callback) ); /****************************************************************************** * FunctionName : seniverse_recv_cb * Description : 处理从心知天气接收的数据 * Parameters : arg -- 传递给回调函数的附加参数 * pusrdata -- 接收到的数据(或连接已关闭时为NULL)! * length -- 接收数据的长度 * Returns : none *******************************************************************************/ //成功接收网络数据的回调函数 LOCAL void ICACHE_FLASH_ATTR seniverse_recv_cb(void *arg, char *pdata, unsigned short length){ struct espconn *pespconn = arg; char *pParse_json=NULL; //char pbuf[2048]; os_printf("len:%u\n",length); if(check_data(pdata, length) == false)//判断接收到的数据长度 { os_printf("seniverse_recv_err\n");//数据接收错误 /* * 在这里实现向服务器重发get请求 * */ return; } //找到json字符串 pParse_json=(char *)os_strstr(pdata, "\r\n\r\n{"); if(NULL == pParse_json) { //出错处理 } pParse_json += 4;//指向"{" // os_printf("%s\n",pParse_json); struct jsontree_context js; //生成 JSON格式数据树 jsontree_setup(&js, (struct jsontree_value *)&seniverse_object_tree, json_putchar); //json_ws_send((struct jsontree_value *)&seniverse_object_tree, "{", pbuf); //os_printf("%s",pbuf); json_parse(&js, pParse_json); }
Esp8266 --深入分析官方json解析源码及如何使用json接口解析心知天气
最新推荐文章于 2024-08-18 10:34:13 发布