ESP8266学习笔记(7)——JSON接口使用

作者: Leung_ManWah | 来源:发表于2018-12-15 16:00 被阅读4次

    一、JSON简介

    JSON(JavaScript Object Notation, JS 对象简谱)是一种轻量级的数据交换格式。它基于 ECMAScript (欧洲计算机协会制定的js规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。

    1.1 JSON 语法规则

    在 JS 语言中,一切都是对象。因此,任何支持的类型都可以通过 JSON 来表示,例如字符串、数字、对象、数组等。但是对象和数组是比较特殊且常用的两种类型:

    ● 对象表示为键值对
    ● 数据由逗号分隔
    ● 花括号保存对象
    ● 方括号保存数组

    1.2 JSON 键/值对

    JSON 键值对是用来保存 JS 对象的一种方式,键/值对组合中的键名写在前面并用双引号 "" 包裹,使用冒号 : 分隔,然后紧接着值:

    {"firstName": "Json"}

    二、JSON接口

    JSON 接口位于 ESP8266_NONOS_SDK/include/json/jsonparse.hjsontree.h

    2.1 相关宏定义

    // json.h 
    #define JSON_TYPE_ARRAY '['    // 数组类型
    #define JSON_TYPE_OBJECT '{'   // 对象类型
    #define JSON_TYPE_PAIR ':'     // 键值对类型
    #define JSON_TYPE_PAIR_NAME 'N' /* for N:V pairs */
    #define JSON_TYPE_STRING '"'
    #define JSON_TYPE_INT 'I'
    #define JSON_TYPE_NUMBER '0'
    #define JSON_TYPE_ERROR 0
    
    #define JSON_TYPE_CALLBACK 'C'  // 回调类型
    
    // jsontree.h 
    /*------------------------- 生成一个JSON键值对 -------------------------*/
    // 第一个参数是键名,第二个参数是键值
    #define JSONTREE_PAIR(name, value) {(name), (struct jsontree_value *)(value)}
    
    /*------------------------- 生成一个回调指针 -------------------------*/
    // 第一个参数JSON输出回调,第二个JSON输入回调,哪个没有就填NULL
    #define JSONTREE_CALLBACK(output, set) {JSON_TYPE_CALLBACK, (output), (set)}
    
    /*------------------------- 生成一个JSON树的对象 -------------------------*/
    // JSONTREE_OBJECT(name, ...) 第一个参数是该对象的名称
    #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 }
    
    /*------------------------- 生成一个JSON键值对数组 -------------------------*/
    #define JSONTREE_PAIR_ARRAY(value) (struct jsontree_value *)(value)
    /*------------------------- 生成一个JSON数组 -------------------------*/
    #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 }
    

    2.2 接口函数








    三、生成JSON数据

    3.1 字符串类型数据

    以ESP8266_NONOS_SDK-2.1.0/example/IoT_Demo目录下 user_webserver.cwifi_station 参数设置为例。
    JSON树结构图:

    /******************************************************************************
     * FunctionName : wifi_station_get
     * Description  : set up the station paramer as a JSON format
     * Parameters   : js_ctx -- A pointer to a JSON set up
     * Returns      : result
    *******************************************************************************/
    LOCAL int ICACHE_FLASH_ATTR
    wifi_station_get(struct jsontree_context *js_ctx)
    {
        const char *path = jsontree_path_name(js_ctx, js_ctx->depth - 1);  // 获取JSON树参数
        struct ip_info ipconfig;                                           // 定义一个IP信息结构体
        uint8 buf[20];
        os_bzero(buf, sizeof(buf));                                        // 清空数组
        wifi_station_get_config(sta_conf);                                 // 查询Wi-Fi Station接口的当前配置参数
        wifi_get_ip_info(STATION_IF, &ipconfig);                           // 查询Wi-Fi Station接口的IP地址
    
        if (os_strncmp(path, "ssid", 4) == 0) {                            // 搜索到有"ssid"这个字符串
            jsontree_write_string(js_ctx, sta_conf->ssid);                 // 将Station接口中ssid这个参数的字符串写入JSON树
        } else if (os_strncmp(path, "password", 8) == 0) {                 // 以下操作同上......
            jsontree_write_string(js_ctx, sta_conf->password);
        } else if (os_strncmp(path, "ip", 2) == 0) {
            os_sprintf(buf, IPSTR, IP2STR(&ipconfig.ip));
            jsontree_write_string(js_ctx, buf);
        } else if (os_strncmp(path, "mask", 4) == 0) {
            os_sprintf(buf, IPSTR, IP2STR(&ipconfig.netmask));
            jsontree_write_string(js_ctx, buf);
        } else if (os_strncmp(path, "gw", 2) == 0) {
            os_sprintf(buf, IPSTR, IP2STR(&ipconfig.gw));
            jsontree_write_string(js_ctx, buf);
        }
    
        return 0;
    }
    
    LOCAL struct jsontree_callback wifi_station_callback =
        JSONTREE_CALLBACK(wifi_station_get, wifi_station_set);  
    // 第一个回调函数用于生成JSON数据格式,第二个回调函数用于解析JSON格式数据(本部分不列出)
    
    JSONTREE_OBJECT(get_station_config_tree,
                    JSONTREE_PAIR("ssid", &wifi_station_callback),
                    JSONTREE_PAIR("password", &wifi_station_callback));
    JSONTREE_OBJECT(ip_tree,
                    JSONTREE_PAIR("ip", &wifi_station_callback),
                    JSONTREE_PAIR("mask", &wifi_station_callback),
                    JSONTREE_PAIR("gw", &wifi_station_callback));
    
    JSONTREE_OBJECT(get_station_tree,
                    JSONTREE_PAIR("Connect_Station", &get_station_config_tree),
                    JSONTREE_PAIR("Ipinfo_Station", &ip_tree));
    
    JSONTREE_OBJECT(get_wifi_station_info_tree,
                    JSONTREE_PAIR("Station", &get_station_tree));
    

    这里生成的JSON是:

    "Station" :
    {
        "Connect_Station" :
        {
            "ssid" : "xxxx" ,
            "password" : "xxxx"
        } ,
        "Ipinfo_Station" :
        {
            "ip" : "xxxx" ,
            "mask" : "xxxx" ,
            "gw" : "xxxx"
        }
    }
    

    "Station" 是用来给后面调用生成Json数据格式的,传输时不存在,即
    "Connect_Station" :{"ssid" : "xxxx" ,"password" : "xxxx"} ,"Ipinfo_Station" :{"ip" : "xxxx" ,"mask" : "xxxx" ,"gw" : "xxxx"}

    /******************************************************************************
     * FunctionName : json_send
     * Description  : processing the data as json format and send to the client or server
     * Parameters   : arg -- argument to set for client or server
     *                ParmType -- json format type
     * Returns      : none
    *******************************************************************************/
    LOCAL void ICACHE_FLASH_ATTR
    json_send(void *arg, ParmType ParmType)
    {
        char *pbuf = NULL;
        pbuf = (char *)os_zalloc(2048);
    
        switch (ParmType) 
        {
            case WIFI:
                json_ws_send((struct jsontree_value *)&get_wifi_station_info_tree, "Station", pbuf);
                break;
            default :
                break;
        }
        data_send(ptrespconn, true, pbuf);    // TCP发送数据
        os_free(pbuf);                        // 释放内存
        pbuf = NULL;
    }
    

    这里有个 json_ws_send 函数,就是生成JSON数据格式字符串,然后给data_send函数TCP发送出去

    /******************************************************************************
     * FunctionName : json_ws_send
     * Description  : set up the JSON format tree for string
     * Parameters   : tree -- A pointer to the JSON format tree
     *                path -- A pointer to the JSON format tree's path
     *                pbuf -- A pointer for the data sent
     * Returns      : none
    *******************************************************************************/
    void ICACHE_FLASH_ATTR
    json_ws_send(struct jsontree_value *tree, const char *path, char *pbuf)
    {
        struct jsontree_context json;
        /* maxsize = 128 bytes */
        json_buf = (char *)os_malloc(jsonSize);
    
        /* reset state and set max-size */
        /* NOTE: packet will be truncated at 512 bytes */
        pos = 0;
        size = jsonSize;
    
        json.values[0] = (struct jsontree_value *)tree;
        jsontree_reset(&json);            // 设置JSON树
        find_json_path(&json, path);      // 查找JSON格式树路径
        json.path = json.depth;
        json.putchar = json_putchar;
    
        while (jsontree_print_next(&json) && json.path <= json.depth);  // 不断获取 JSON 树下⼀个元素
    
        json_buf[pos] = 0;
        os_memcpy(pbuf, json_buf, pos);
        os_free(json_buf);
    }
    

    3.2 整型数据

    以ESP8266_NONOS_SDK-2.1.0/example/IoT_Demo目录下 user_webserver.cswitch 参数设置为例。

    /******************************************************************************
     * FunctionName : status_get
     * Description  : set up the device status as a JSON format
     * Parameters   : js_ctx -- A pointer to a JSON set up
     * Returns      : result
    *******************************************************************************/
    LOCAL int ICACHE_FLASH_ATTR
    status_get(struct jsontree_context *js_ctx)
    {
        if (user_plug_get_status() == 1) {    // 获取继电器状态
            jsontree_write_int(js_ctx, 1);
        } else {
            jsontree_write_int(js_ctx, 0);
        }
    
        return 0;
    }
    

    生成JSON格式:

    "status" :0/1
    

    3.3 数组类型数据

    3.3.1 整型数组

    LOCAL int ICACHE_FLASH_ATTR
    jsonTree_get(struct jsontree_context *js_ctx)
    {
        const char *path = jsontree_path_name(js_ctx, js_ctx->depth - 1);
        
        //生成"String":"data"
        if (os_strncmp(path, "String", os_strlen("String")) == 0) {
            jsontree_write_string(js_ctx, "data");
        //生成"Integer":1
        } else if (os_strncmp(path, "Integer", os_strlen("Integer")) == 0) {
            jsontree_write_int(js_ctx, 1);
        //生成"Array":[0,1,2]
        } else if (os_strncmp(path, "Array", os_strlen("Array")) == 0) {
            int array[3] = {0,1,2};
            jsontree_write_atom(js_ctx, "[");
            jsontree_write_int_array(js_ctx, array, 3);
            jsontree_write_atom(js_ctx, "]");
        }
    
        return 0;
    }
    

    生成JSON格式:

    "String": "data",
    "Integer": 1,
    "Array": [0, 1, 2]
    

    3.3.2 字符串数组

    LOCAL int ICACHE_FLASH_ATTR
    jsonArray_get(struct jsontree_context *js_ctx)
    {
        const char *path = jsontree_path_name(js_ctx, js_ctx->depth - 1);
    
        if (os_strncmp(path, "K1", os_strlen("K2")) == 0) {
            jsontree_write_string(js_ctx, "D1");
        } else if (os_strncmp(path, "K2", os_strlen("K2")) == 0) {
            jsontree_write_string(js_ctx, "D2");
        } else if (os_strncmp(path, "K3", os_strlen("K3")) == 0) {
            jsontree_write_string(js_ctx, "D3");
        }
    
        return 0;
    }
    
    //初始化一个Json数据回调函数
    //JSONTREE_CALLBACK第一个参数为生成Json数据的函数指针,第二个为获取Json数据的函数指针
    LOCAL struct jsontree_callback jsonArrayCallback =
        JSONTREE_CALLBACK(jsonArray_get, NULL);
    
    JSONTREE_OBJECT(jsonArrayData,
                    JSONTREE_PAIR("K1", &jsonArrayCallback),
                    JSONTREE_PAIR("K2", &jsonArrayCallback),
                    JSONTREE_PAIR("K3", &jsonArrayCallback));
    JSONTREE_ARRAY(jsonArray,
                   JSONTREE_PAIR_ARRAY(&jsonArrayData),
                   JSONTREE_PAIR_ARRAY(&jsonArrayData),
                   JSONTREE_PAIR_ARRAY(&jsonArrayData));
    
    LOCAL struct jsontree_callback jsonCallback =
        JSONTREE_CALLBACK(jsonTree_get, NULL);
    
    JSONTREE_OBJECT(jsonObject,
                    JSONTREE_PAIR("String", &jsonCallback),
                    JSONTREE_PAIR("Integer", &jsonCallback),
                    JSONTREE_PAIR("JsonArray", &jsonArray));
    

    生成JSON格式:

    "String": "data",
    "Integer": 1,
    "JsonArray": [
    {
        "K1": "D1",
        "K2": "D2",
        "K3": "D3"
    },
    {
        "K1": "D1",
        "K2": "D2",
        "K3": "D3"
    },
    {
      "K1": "D1",
      "K2": "D2",
      "K3": "D3"
    }
    ]
    

    四、解析JSON数据

    以ESP8266_NONOS_SDK-2.1.0/example/IoT_Demo目录下 user_webserver.cwifi_station 参数设置为例。

    LOCAL void ICACHE_FLASH_ATTR
    webserver_recv(void *arg, char *pusrdata, unsigned short length)
    {
        ...
        ...
        else if (os_strcmp(pURL_Frame->pFilename, "wifi") == 0) 
        {
            if (pParseBuffer != NULL) 
            {
                struct jsontree_context js;
                ...
                jsontree_setup(&js, (struct jsontree_value *)&wifi_req_tree, json_putchar);  // ⽣成JSON格式数据树
                json_parse(&js, pParseBuffer);    // 解析JSON格式数据
                ...
            }
        }
    }
    

    这里有个 json_parse 函数,就是解析JSON格式数据

    /******************************************************************************
     * FunctionName : json_parse
     * Description  : parse the data as a JSON format
     * Parameters   : js_ctx -- A pointer to a JSON set up
     *                ptrJSONMessage -- A pointer to the data
     * Returns      : none
    *******************************************************************************/
    void ICACHE_FLASH_ATTR
    json_parse(struct jsontree_context *json, char *ptrJSONMessage)
    {
        /* Set value */
        struct jsontree_value *v;
        struct jsontree_callback *c;
        struct jsontree_callback *c_bak = NULL;
    
        while ((v = jsontree_find_next(json, JSON_TYPE_CALLBACK)) != NULL) {  // 查找JSON树元素
            c = (struct jsontree_callback *)v;
    
            if (c == c_bak) {
                continue;
            }
    
            c_bak = c;
    
            if (c->set != NULL) {
                struct jsonparse_state js;
    
                jsonparse_setup(&js, ptrJSONMessage, os_strlen(ptrJSONMessage));  // JSON解析初始化
                c->set(json, &js);
            }
        }
    }
    

    JSON树结构图:

    /******************************************************************************
     * FunctionName : wifi_station_set
     * Description  : parse the station parmer as a JSON format
     * Parameters   : js_ctx -- A pointer to a JSON set up
     *                parser -- A pointer to a JSON parser state
     * Returns      : result
    *******************************************************************************/
    LOCAL int ICACHE_FLASH_ATTR
    wifi_station_set(struct jsontree_context *js_ctx, struct jsonparse_state *parser)
    {
        int type;
        uint8 station_tree;
    
        while ((type = jsonparse_next(parser)) != 0) {  // 解析 JSON 格式下⼀个元素
            if (type == JSON_TYPE_PAIR_NAME) {
                char buffer[64];
                os_bzero(buffer, 64);
    
                if (jsonparse_strcmp_value(parser, "Station") == 0) {  // ⽐较解析 JSON 数据与特定字符串
                    station_tree = 1;
                } else if (jsonparse_strcmp_value(parser, "Softap") == 0) {
                    station_tree = 0;
                }
    
                if (station_tree) {
                    if (jsonparse_strcmp_value(parser, "ssid") == 0) {  // ⽐较解析 JSON 数据与特定字符串
                        jsonparse_next(parser);
                        jsonparse_next(parser);
                        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));
                    }
    
    #if ESP_PLATFORM
                    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);
                    }
    #endif
                }
            }
        }
    
        return 0;
    }
    
    LOCAL struct jsontree_callback wifi_station_callback =
        JSONTREE_CALLBACK(wifi_station_get, wifi_station_set);
    // 第一个回调函数用于生成JSON数据格式(上一部分已列出),第二个回调函数用于解析JSON格式数据
    
    JSONTREE_OBJECT(set_station_config_tree,
                    JSONTREE_PAIR("ssid", &wifi_station_callback),
                    JSONTREE_PAIR("password", &wifi_station_callback),
                    JSONTREE_PAIR("token", &wifi_station_callback));
    
    JSONTREE_OBJECT(set_station_tree,
                    JSONTREE_PAIR("Connect_Station", &set_station_config_tree));
    
    JSONTREE_OBJECT(set_wifi_tree,
                    JSONTREE_PAIR("Station", &set_station_tree),
                    JSONTREE_PAIR("Softap", &set_softap_tree));
    
    JSONTREE_OBJECT(wifi_request_tree,
                    JSONTREE_PAIR("Request", &set_wifi_tree));
    
    JSONTREE_OBJECT(wifi_req_tree,
                    JSONTREE_PAIR("wifi", &wifi_request_tree));
    

    这里解析的JSON是:

    "wifi" :
    {
        "Request" :
        {
            "Station" : 
            {
                "Connect_Station" : 
                {
                    "ssid" : "xxxx" ,
                    "password" : "xxxx"
                    "token" : "xxxx"
                }
            }   
        } 
    }
    

    • 由 Leung 写于 2018 年 12 月 15 日

    • 参考:ESP8266 Non-OS SDK API参考[zj6w]
        【ESP8266】使用ESP8266 NONOS SDK的JSON API

    相关文章

      网友评论

        本文标题:ESP8266学习笔记(7)——JSON接口使用

        本文链接:https://www.haomeiwen.com/subject/lmiohqtx.html