From e1169d4b1164195ca114cc223b0bfe022c199fe9 Mon Sep 17 00:00:00 2001 From: Bernhard Kirchen Date: Tue, 7 May 2024 20:39:18 +0200 Subject: [PATCH] Fix: restore JSON array access for HTTP power meter this implements accessing array members in an ArduinoJSON object following the FirebaseJson syntax. the FirebaseJson lib was previously removed to save flash memory, and logic was implemented to find a JSON node using the FirebaseJson path syntax, restoring the functionality. however, array access was not implemented. this change also addresses leading and trailing and double forward slashes in the path expression. moreover, much more expressive error messages are now generated in case the path could not be resolved. --- src/HttpPowerMeter.cpp | 52 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/src/HttpPowerMeter.cpp b/src/HttpPowerMeter.cpp index dbe9aa79a..e3033cbb9 100644 --- a/src/HttpPowerMeter.cpp +++ b/src/HttpPowerMeter.cpp @@ -233,23 +233,61 @@ bool HttpPowerMeterClass::tryGetFloatValueForPhase(int phase, String jsonPath, U int end = jsonPath.indexOf(delimiter); auto value = root.as(); + auto getNext = [this, &value, &jsonPath, &start](String const& key) -> bool { + // handle double forward slashes and paths starting or ending with a slash + if (key.isEmpty()) { return true; } + + if (key[0] == '[' && key[key.length() - 1] == ']') { + if (!value.is()) { + snprintf_P(httpPowerMeterError, sizeof(httpPowerMeterError), + PSTR("[HttpPowerMeter] Cannot access non-array JSON node " + "using array index '%s' (JSON path '%s', position %i)"), + key.c_str(), jsonPath.c_str(), start); + return false; + } + + auto idx = key.substring(1, key.length() - 1).toInt(); + value = value[idx]; + + if (value.isNull()) { + snprintf_P(httpPowerMeterError, sizeof(httpPowerMeterError), + PSTR("[HttpPowerMeter] Unable to access JSON array " + "index %li (JSON path '%s', position %i)"), + idx, jsonPath.c_str(), start); + return false; + } + + return true; + } + + value = value[key]; + + if (value.isNull()) { + snprintf_P(httpPowerMeterError, sizeof(httpPowerMeterError), + PSTR("[HttpPowerMeter] Unable to access JSON key " + "'%s' (JSON path '%s', position %i)"), + key.c_str(), jsonPath.c_str(), start); + return false; + } + + return true; + }; + // NOTE: "Because ArduinoJson implements the Null Object Pattern, it is // always safe to read the object: if the key doesn't exist, it returns an // empty value." while (end != -1) { - String key = jsonPath.substring(start, end); - value = value[key]; + if (!getNext(jsonPath.substring(start, end))) { return false; } start = end + 1; end = jsonPath.indexOf(delimiter, start); } - String lastKey = jsonPath.substring(start); - value = value[lastKey]; + if (!getNext(jsonPath.substring(start))) { return false; } - if (value.isNull()) { + if (!value.is()) { snprintf_P(httpPowerMeterError, sizeof(httpPowerMeterError), - PSTR("[HttpPowerMeter] Unable to find a value for phase %i with JSON path \"%s\""), - phase+1, jsonPath.c_str()); + PSTR("[HttpPowerMeter] not a float: '%s'"), + value.as().c_str()); return false; }