diff --git a/lib/jsmn-shadinger-1.0/README.md b/lib/jsmn-shadinger-1.0/README.md index f98503eb1..bf5b59565 100644 --- a/lib/jsmn-shadinger-1.0/README.md +++ b/lib/jsmn-shadinger-1.0/README.md @@ -1,9 +1,15 @@ # JSMN lightweight JSON parser for Tasmota -Intro: +Intro: this library uses the JSMN in-place JSON parser. +See https://github.com/zserge/jsmn and https://zserge.com/jsmn/ + +It is proposed as a replacement for ArduinoJson. It has less features, does only parsing but does it in a very efficient way. ## Benefits +First, the memory impact is very low: 4 bytes per token and no need to add an extra buffer for values. +Second, the code is much smaller than ArduinoJson by 5-7KB. + ## How to use `{"Device":"0x1234","Power":true,"Temperature":25.5}` @@ -24,7 +30,8 @@ If what you need is to parse a JSON Object for values with default values: #include "JsonParser.h" char json_buffer[] = "{\"Device\":\"0x1234\",\"Power\":true,\"Temperature\":25.6}"; -JsonParserObject root = JsonParser(json_buffer).getRootObject(); +JsonParser parser(json_buffer); +JsonParserObject root = parser.getRootObject(); if (!root) { ResponseCmndChar_P(PSTR("Invalid JSON")); return; } uint16_t d = root.getUInt(PSTR("DEVICE"), 0xFFFF); // case insensitive @@ -37,7 +44,8 @@ Alternative pattern, if you want to test the existence of the attribute first: #include "JsonParser.h" char json_buffer[] = "{\"Device\":\"0x1234\",\"Power\":true,\"Temperature\":25.6}"; -JsonParserObject root = JsonParser(json_buffer).getRootObject(); +JsonParser parser(json_buffer); +JsonParserObject root = parser.getRootObject(); if (!root) { ResponseCmndChar_P(PSTR("Invalid JSON")); return; } JsonParserToken val = root[PSTR("DEVICE")]; @@ -54,6 +62,12 @@ if (val) { } ``` +WARNING: never use the following form: +``` +JsonParserObject root = JsonParser(json_buffer).getRootObject(); +``` +In this case, the `JsonParser` object is temporary and destroyed at the end of the expression. Setting the JsonParser to a local variable ensures that the lifetime of the object is extended to the end of the scope. + ## Types and conversion JSMN relies on the concept of JSON Tokens `JsonParserToken`. Tokens do not hold any data, but point to token boundaries in JSON string instead. Every jsmn token has a type, which indicates the type of corresponding JSON token. JSMN for Tasmota extends the type from JSMN to ease further parsing. diff --git a/lib/jsmn-shadinger-1.0/test/test-json.cpp b/lib/jsmn-shadinger-1.0/test/test-json.cpp index 9ede0a2bb..ccfbf45fd 100644 --- a/lib/jsmn-shadinger-1.0/test/test-json.cpp +++ b/lib/jsmn-shadinger-1.0/test/test-json.cpp @@ -15,7 +15,7 @@ int main(int argc, char* argv[]) { // char * json_str = test_complex; char * json_str = test_simple; - JsonParser parser(64); // size for 64 tokens + // JsonParser parser(64); // size for 64 tokens int r = parser.parse(json_str); diff --git a/tasmota/support.ino b/tasmota/support.ino index 18c5469ae..23b8780fa 100644 --- a/tasmota/support.ino +++ b/tasmota/support.ino @@ -1473,7 +1473,8 @@ bool JsonTemplate(char* dataBuf) if (strlen(dataBuf) < 9) { return false; } // Workaround exception if empty JSON like {} - Needs checks - JsonParserObject root = JsonParser((char*) dataBuf).getRootObject(); + JsonParser parser((char*) dataBuf); + JsonParserObject root = parser.getRootObject(); if (!root) { return false; } // All parameters are optional allowing for partial changes diff --git a/tasmota/xdrv_01_webserver.ino b/tasmota/xdrv_01_webserver.ino index 16daa8f6f..f1aff262b 100644 --- a/tasmota/xdrv_01_webserver.ino +++ b/tasmota/xdrv_01_webserver.ino @@ -27,8 +27,6 @@ #define XDRV_01 1 -#include "JsonParser.h" - #ifndef WIFI_SOFT_AP_CHANNEL #define WIFI_SOFT_AP_CHANNEL 1 // Soft Access Point Channel number between 1 and 11 as used by WifiManager web GUI #endif @@ -3366,7 +3364,8 @@ bool JsonWebColor(const char* dataBuf) } } #else - JsonParserObject root = JsonParser((char*) dataBuf).getRootObject(); + JsonParser parser((char*) dataBuf); + JsonParserObject root = parser.getRootObject(); JsonParserArray arr = root[PSTR(D_CMND_WEBCOLOR)].getArray(); if (arr) { // if arr is valid, i.e. json is valid, the key D_CMND_WEBCOLOR was found and the token is an arra uint32_t i = 0; diff --git a/tasmota/xdrv_05_irremote.ino b/tasmota/xdrv_05_irremote.ino index ebd1f06df..0c96e7d2a 100644 --- a/tasmota/xdrv_05_irremote.ino +++ b/tasmota/xdrv_05_irremote.ino @@ -25,7 +25,6 @@ #define XDRV_05 5 #include -#include "JsonParser.h" enum IrErrors { IE_NO_ERROR, IE_INVALID_RAWDATA, IE_INVALID_JSON, IE_SYNTAX_IRSEND }; @@ -204,7 +203,8 @@ uint32_t IrRemoteCmndIrSendJson(void) uint16_t repeat = root[UpperCase_P(parm_uc, PSTR(D_JSON_IR_REPEAT))]; #else RemoveSpace(XdrvMailbox.data); // TODO is this really needed? - JsonParserObject root = JsonParser((char*) XdrvMailbox.data).getRootObject(); + JsonParser parser(XdrvMailbox.data); + JsonParserObject root = parser.getRootObject(); if (!root) { return IE_INVALID_JSON; } // IRsend { "protocol": "SAMSUNG", "bits": 32, "data": 551502015 } diff --git a/tasmota/xdrv_05_irremote_full.ino b/tasmota/xdrv_05_irremote_full.ino index d72711fc8..275112bab 100644 --- a/tasmota/xdrv_05_irremote_full.ino +++ b/tasmota/xdrv_05_irremote_full.ino @@ -29,7 +29,6 @@ #include #include #include -#include "JsonParser.h" enum IrErrors { IE_RESPONSE_PROVIDED, IE_NO_ERROR, IE_INVALID_RAWDATA, IE_INVALID_JSON, IE_SYNTAX_IRSEND, IE_SYNTAX_IRHVAC, IE_UNSUPPORTED_HVAC, IE_UNSUPPORTED_PROTOCOL }; @@ -288,7 +287,8 @@ uint32_t IrRemoteCmndIrHvacJson(void) stdAc::state_t state, prev; //AddLog_P2(LOG_LEVEL_DEBUG, PSTR("IRHVAC: Received %s"), XdrvMailbox.data); - JsonParserObject root = JsonParser((char*) XdrvMailbox.data).getRootObject(); + JsonParser parser(XdrvMailbox.data); + JsonParserObject root = parser.getRootObject(); if (!root) { return IE_INVALID_JSON; } // from: https://github.com/crankyoldgit/IRremoteESP8266/blob/master/examples/CommonAcControl/CommonAcControl.ino diff --git a/tasmota/xdrv_09_timers.ino b/tasmota/xdrv_09_timers.ino index 79efc1f1c..b9d6d57c7 100644 --- a/tasmota/xdrv_09_timers.ino +++ b/tasmota/xdrv_09_timers.ino @@ -425,7 +425,8 @@ void CmndTimer(void) #if defined(USE_RULES)==0 && defined(USE_SCRIPT)==0 if (devices_present) { #endif - JsonParserObject root = JsonParser(XdrvMailbox.data).getRootObject(); + JsonParser parser(XdrvMailbox.data); + JsonParserObject root = parser.getRootObject(); if (!root) { Response_P(PSTR("{\"" D_CMND_TIMER "%d\":\"" D_JSON_INVALID_JSON "\"}"), index); // JSON decode failed error = 1; diff --git a/tasmota/xdrv_10_rules.ino b/tasmota/xdrv_10_rules.ino index abc6fcece..7377585c5 100644 --- a/tasmota/xdrv_10_rules.ino +++ b/tasmota/xdrv_10_rules.ino @@ -532,7 +532,7 @@ bool RulesRuleMatch(uint8_t rule_set, String &event, String &rule) #else String buf = event; // copy the string into a new buffer that will be modified - JsonParser parser = JsonParser((char*)buf.c_str()); + JsonParser parser((char*)buf.c_str()); JsonParserObject obj = parser.getRootObject(); if (!obj) { AddLog_P2(LOG_LEVEL_DEBUG, PSTR("RUL: Event too long (%d)"), event.length()); @@ -1069,7 +1069,8 @@ bool RulesMqttData(void) if (event_item.Key.length() == 0) { //If did not specify Key value = sData; } else { //If specified Key, need to parse Key/Value from JSON data - JsonParserObject jsonData = JsonParser((char*)sData.c_str()).getRootObject(); + JsonParser parser((char*)sData.c_str()); + JsonParserObject jsonData = parser.getRootObject(); String key1 = event_item.Key; String key2; diff --git a/tasmota/xdrv_17_rcswitch.ino b/tasmota/xdrv_17_rcswitch.ino index af89e0a9f..af3dc1bba 100644 --- a/tasmota/xdrv_17_rcswitch.ino +++ b/tasmota/xdrv_17_rcswitch.ino @@ -104,7 +104,8 @@ void CmndRfSend(void) int repeat = 10; int pulse = 350; - JsonParserObject root = JsonParser(XdrvMailbox.data).getRootObject(); + JsonParser parser(XdrvMailbox.data); + JsonParserObject root = parser.getRootObject(); if (root) { // RFsend {"data":0x501014,"bits":24,"protocol":1,"repeat":10,"pulse":350} char parm_uc[10]; diff --git a/tasmota/xdrv_20_hue.ino b/tasmota/xdrv_20_hue.ino index 6bdc420af..7c2e29395 100644 --- a/tasmota/xdrv_20_hue.ino +++ b/tasmota/xdrv_20_hue.ino @@ -569,7 +569,7 @@ void HueLightsCommand(uint8_t device, uint32_t device_id, String &response) { if (Webserver->args()) { response = "["; - JsonParser parser = JsonParser((char*) Webserver->arg((Webserver->args())-1).c_str()); + JsonParser parser((char*) Webserver->arg((Webserver->args())-1).c_str()); JsonParserObject root = parser.getRootObject(); JsonParserToken hue_on = root[PSTR("on")]; diff --git a/tasmota/xdrv_23_zigbee_2_devices.ino b/tasmota/xdrv_23_zigbee_2_devices.ino index e3182faad..e68342da6 100644 --- a/tasmota/xdrv_23_zigbee_2_devices.ino +++ b/tasmota/xdrv_23_zigbee_2_devices.ino @@ -19,8 +19,6 @@ #ifdef USE_ZIGBEE -#include "JsonParser.h" - #ifndef ZIGBEE_SAVE_DELAY_SECONDS #define ZIGBEE_SAVE_DELAY_SECONDS 2 // wait for 2s before saving Zigbee info #endif diff --git a/tasmota/xdrv_23_zigbee_3_hue.ino b/tasmota/xdrv_23_zigbee_3_hue.ino index 4b95a58be..1f4b18e19 100644 --- a/tasmota/xdrv_23_zigbee_3_hue.ino +++ b/tasmota/xdrv_23_zigbee_3_hue.ino @@ -218,7 +218,7 @@ void ZigbeeHandleHue(uint16_t shortaddr, uint32_t device_id, String &response) { if (Webserver->args()) { response = "["; - JsonParser parser = JsonParser((char*) Webserver->arg((Webserver->args())-1).c_str()); + JsonParser parser((char*) Webserver->arg((Webserver->args())-1).c_str()); JsonParserObject root = parser.getRootObject(); JsonParserToken hue_on = root[PSTR("on")]; diff --git a/tasmota/xdrv_23_zigbee_A_impl.ino b/tasmota/xdrv_23_zigbee_A_impl.ino index b35296370..90cc4e28c 100644 --- a/tasmota/xdrv_23_zigbee_A_impl.ino +++ b/tasmota/xdrv_23_zigbee_A_impl.ino @@ -21,8 +21,6 @@ #define XDRV_23 23 -#include "JsonParser.h" - const char kZbCommands[] PROGMEM = D_PRFX_ZB "|" // prefix #ifdef USE_ZIGBEE_ZNP D_CMND_ZIGBEEZNPSEND "|" D_CMND_ZIGBEEZNPRECEIVE "|" @@ -626,7 +624,8 @@ void CmndZbSend(void) { // ZbSend { "device":"0x1234", "endpoint":"0x03", "send":{"Color":"1,2"} } // ZbSend { "device":"0x1234", "endpoint":"0x03", "send":{"Color":"0x1122,0xFFEE"} } if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } - JsonParserObject root = JsonParser(XdrvMailbox.data).getRootObject(); + JsonParser parser(XdrvMailbox.data); + JsonParserObject root = parser.getRootObject(); if (!root) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; } // params @@ -748,7 +747,8 @@ void ZbBindUnbind(bool unbind) { // false = bind, true = unbind // local endpoint is always 1, IEEE addresses are calculated if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } - JsonParserObject root = JsonParser(XdrvMailbox.data).getRootObject(); + JsonParser parser(XdrvMailbox.data); + JsonParserObject root = parser.getRootObject(); if (!root) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; } // params @@ -1064,10 +1064,10 @@ void CmndZbSave(void) { // ZbRestore {"Device":"0x5ADF","Name":"Petite_Lampe","IEEEAddr":"0x90FD9FFFFE03B051","ModelId":"TRADFRI bulb E27 WS opal 980lm","Manufacturer":"IKEA of Sweden","Endpoints":["0x01","0xF2"]} void CmndZbRestore(void) { if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } - JsonParser p(XdrvMailbox.data); - JsonParserToken root = p.getRoot(); + JsonParser parser(XdrvMailbox.data); + JsonParserToken root = parser.getRoot(); - if (!p || !(root.isObject() || root.isArray())) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; } + if (!parser || !(root.isObject() || root.isArray())) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; } // Check is root contains `ZbStatus` key, if so change the root JsonParserToken zbstatus = root.getObject().findStartsWith(PSTR("ZbStatus")); @@ -1219,7 +1219,8 @@ void CmndZbConfig(void) { // if (zigbee.init_phase) { ResponseCmndChar_P(PSTR(D_ZIGBEE_NOT_STARTED)); return; } RemoveAllSpaces(XdrvMailbox.data); if (strlen(XdrvMailbox.data) > 0) { - JsonParserObject root = JsonParser(XdrvMailbox.data).getRootObject(); + JsonParser parser(XdrvMailbox.data); + JsonParserObject root = parser.getRootObject(); if (!root) { ResponseCmndChar_P(PSTR(D_JSON_INVALID_JSON)); return; } // Channel diff --git a/tasmota/xdrv_39_thermostat.ino b/tasmota/xdrv_39_thermostat.ino index 9c70fcb2d..4a29ad36b 100644 --- a/tasmota/xdrv_39_thermostat.ino +++ b/tasmota/xdrv_39_thermostat.ino @@ -1328,7 +1328,8 @@ void ThermostatDebug(uint8_t ctr_output) #endif // DEBUG_THERMOSTAT void ThermostatGetLocalSensor(uint8_t ctr_output) { - JsonParserObject root = JsonParser(mqtt_data).getRootObject(); + JsonParser parser(mqtt_data); + JsonParserObject root = parser.getRootObject(); if (root) { JsonParserToken value_token = root[PSTR(THERMOSTAT_SENSOR_NAME)].getObject()[PSTR("Temperature")]; if (value_token.isNum()) { diff --git a/tasmota/xdrv_40_telegram.ino b/tasmota/xdrv_40_telegram.ino index e1716f56d..9834d3b30 100644 --- a/tasmota/xdrv_40_telegram.ino +++ b/tasmota/xdrv_40_telegram.ino @@ -226,7 +226,8 @@ void TelegramAnalizeMessage(void) { Telegram.message[i][5] = ""; String buf = Telegram.message[i][0]; // we need to keep a copy of the buffer - JsonParserObject root = JsonParser((char*)buf.c_str()).getRootObject(); + JsonParser parser((char*)buf.c_str()); + JsonParserObject root = parser.getRootObject(); if (root) { Telegram.message[i][0] = root["update_id"].getStr(); Telegram.message[i][1] = root["message"].getObject()["from"].getObject()["id"].getStr();