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

一、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参考[7qq6]
    【ESP8266】使用ESP8266 NONOS SDK的JSON API

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容