forked from bblanchon/ArduinoJson
Merge branch 'master' into 6.x
# Conflicts: # CHANGELOG.md # src/ArduinoJson/Deserialization/JsonParser.hpp # src/ArduinoJson/Deserialization/JsonParserImpl.hpp # test/JsonBuffer/nestingLimit.cpp
This commit is contained in:
@ -4,6 +4,8 @@ ArduinoJson: change log
|
|||||||
HEAD
|
HEAD
|
||||||
----
|
----
|
||||||
|
|
||||||
|
* Fixed `JsonBuffer::parse()` not respecting nesting limit correctly (issue #693)
|
||||||
|
* Fixed inconsistencies in nesting level counting (PR #695 from Zhenyu Wu)
|
||||||
* Added `DynamicJsonArray` and `StaticJsonArray`
|
* Added `DynamicJsonArray` and `StaticJsonArray`
|
||||||
* Added `DynamicJsonObject` and `StaticJsonObject`
|
* Added `DynamicJsonObject` and `StaticJsonObject`
|
||||||
* Added `DynamicJsonVariant` and `StaticJsonVariant`
|
* Added `DynamicJsonVariant` and `StaticJsonVariant`
|
||||||
|
@ -38,12 +38,9 @@ class JsonParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const char *parseString();
|
const char *parseString();
|
||||||
JsonError parseAnythingTo(JsonVariant *destination);
|
JsonError parseArray(JsonVariant &variant);
|
||||||
FORCE_INLINE JsonError parseAnythingToUnsafe(JsonVariant *destination);
|
JsonError parseObject(JsonVariant &variant);
|
||||||
|
JsonError parseValue(JsonVariant &variant);
|
||||||
inline JsonError parseArrayTo(JsonVariant *destination);
|
|
||||||
inline JsonError parseObjectTo(JsonVariant *destination);
|
|
||||||
inline JsonError parseStringTo(JsonVariant *destination);
|
|
||||||
|
|
||||||
static inline bool isBetween(char c, char min, char max) {
|
static inline bool isBetween(char c, char min, char max) {
|
||||||
return min <= c && c <= max;
|
return min <= c && c <= max;
|
||||||
|
@ -17,38 +17,11 @@ inline bool ArduinoJson::Internals::JsonParser<TReader, TWriter>::eat(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TReader, typename TWriter>
|
|
||||||
inline ArduinoJson::JsonError
|
|
||||||
ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseAnythingTo(
|
|
||||||
JsonVariant *destination) {
|
|
||||||
if (_nestingLimit == 0) return JsonError::TooDeep;
|
|
||||||
_nestingLimit--;
|
|
||||||
JsonError error = parseAnythingToUnsafe(destination);
|
|
||||||
_nestingLimit++;
|
|
||||||
return error;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename TReader, typename TWriter>
|
|
||||||
inline ArduinoJson::JsonError
|
|
||||||
ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseAnythingToUnsafe(
|
|
||||||
JsonVariant *destination) {
|
|
||||||
skipSpacesAndComments(_reader);
|
|
||||||
|
|
||||||
switch (_reader.current()) {
|
|
||||||
case '[':
|
|
||||||
return parseArrayTo(destination);
|
|
||||||
|
|
||||||
case '{':
|
|
||||||
return parseObjectTo(destination);
|
|
||||||
|
|
||||||
default:
|
|
||||||
return parseStringTo(destination);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename TReader, typename TWriter>
|
template <typename TReader, typename TWriter>
|
||||||
inline ArduinoJson::JsonError
|
inline ArduinoJson::JsonError
|
||||||
ArduinoJson::Internals::JsonParser<TReader, TWriter>::parse(JsonArray &array) {
|
ArduinoJson::Internals::JsonParser<TReader, TWriter>::parse(JsonArray &array) {
|
||||||
|
if (_nestingLimit == 0) return JsonError::TooDeep;
|
||||||
|
|
||||||
// Check opening braket
|
// Check opening braket
|
||||||
if (!eat('[')) return JsonError::OpeningBracketExpected;
|
if (!eat('[')) return JsonError::OpeningBracketExpected;
|
||||||
if (eat(']')) return JsonError::Ok;
|
if (eat(']')) return JsonError::Ok;
|
||||||
@ -57,7 +30,9 @@ ArduinoJson::Internals::JsonParser<TReader, TWriter>::parse(JsonArray &array) {
|
|||||||
for (;;) {
|
for (;;) {
|
||||||
// 1 - Parse value
|
// 1 - Parse value
|
||||||
JsonVariant value;
|
JsonVariant value;
|
||||||
JsonError error = parseAnythingTo(&value);
|
_nestingLimit--;
|
||||||
|
JsonError error = parse(value);
|
||||||
|
_nestingLimit++;
|
||||||
if (error != JsonError::Ok) return error;
|
if (error != JsonError::Ok) return error;
|
||||||
if (!array.add(value)) return JsonError::NoMemory;
|
if (!array.add(value)) return JsonError::NoMemory;
|
||||||
|
|
||||||
@ -71,6 +46,8 @@ template <typename TReader, typename TWriter>
|
|||||||
inline ArduinoJson::JsonError
|
inline ArduinoJson::JsonError
|
||||||
ArduinoJson::Internals::JsonParser<TReader, TWriter>::parse(
|
ArduinoJson::Internals::JsonParser<TReader, TWriter>::parse(
|
||||||
JsonObject &object) {
|
JsonObject &object) {
|
||||||
|
if (_nestingLimit == 0) return JsonError::TooDeep;
|
||||||
|
|
||||||
// Check opening brace
|
// Check opening brace
|
||||||
if (!eat('{')) return JsonError::OpeningBraceExpected;
|
if (!eat('{')) return JsonError::OpeningBraceExpected;
|
||||||
if (eat('}')) return JsonError::Ok;
|
if (eat('}')) return JsonError::Ok;
|
||||||
@ -84,7 +61,9 @@ ArduinoJson::Internals::JsonParser<TReader, TWriter>::parse(
|
|||||||
|
|
||||||
// 2 - Parse value
|
// 2 - Parse value
|
||||||
JsonVariant value;
|
JsonVariant value;
|
||||||
JsonError error = parseAnythingTo(&value);
|
_nestingLimit--;
|
||||||
|
JsonError error = parse(value);
|
||||||
|
_nestingLimit++;
|
||||||
if (error != JsonError::Ok) return error;
|
if (error != JsonError::Ok) return error;
|
||||||
if (!object.set(key, value)) return JsonError::NoMemory;
|
if (!object.set(key, value)) return JsonError::NoMemory;
|
||||||
|
|
||||||
@ -98,29 +77,55 @@ template <typename TReader, typename TWriter>
|
|||||||
inline ArduinoJson::JsonError
|
inline ArduinoJson::JsonError
|
||||||
ArduinoJson::Internals::JsonParser<TReader, TWriter>::parse(
|
ArduinoJson::Internals::JsonParser<TReader, TWriter>::parse(
|
||||||
JsonVariant &variant) {
|
JsonVariant &variant) {
|
||||||
return parseAnythingTo(&variant);
|
skipSpacesAndComments(_reader);
|
||||||
|
|
||||||
|
switch (_reader.current()) {
|
||||||
|
case '[':
|
||||||
|
return parseArray(variant);
|
||||||
|
|
||||||
|
case '{':
|
||||||
|
return parseObject(variant);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return parseValue(variant);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TReader, typename TWriter>
|
template <typename TReader, typename TWriter>
|
||||||
inline ArduinoJson::JsonError
|
inline ArduinoJson::JsonError
|
||||||
ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseArrayTo(
|
ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseArray(
|
||||||
JsonVariant *destination) {
|
JsonVariant &variant) {
|
||||||
JsonArray *array = new (_buffer) JsonArray(_buffer);
|
JsonArray *array = new (_buffer) JsonArray(_buffer);
|
||||||
if (!array) return JsonError::NoMemory;
|
if (!array) return JsonError::NoMemory;
|
||||||
*destination = array;
|
variant = array;
|
||||||
return parse(*array);
|
return parse(*array);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TReader, typename TWriter>
|
template <typename TReader, typename TWriter>
|
||||||
inline ArduinoJson::JsonError
|
inline ArduinoJson::JsonError
|
||||||
ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseObjectTo(
|
ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseObject(
|
||||||
JsonVariant *destination) {
|
JsonVariant &variant) {
|
||||||
JsonObject *object = new (_buffer) JsonObject(_buffer);
|
JsonObject *object = new (_buffer) JsonObject(_buffer);
|
||||||
if (!object) return JsonError::NoMemory;
|
if (!object) return JsonError::NoMemory;
|
||||||
*destination = object;
|
variant = object;
|
||||||
return parse(*object);
|
return parse(*object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename TReader, typename TWriter>
|
||||||
|
inline ArduinoJson::JsonError
|
||||||
|
ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseValue(
|
||||||
|
JsonVariant &variant) {
|
||||||
|
bool hasQuotes = isQuote(_reader.current());
|
||||||
|
const char *value = parseString();
|
||||||
|
if (value == NULL) return JsonError::NoMemory;
|
||||||
|
if (hasQuotes) {
|
||||||
|
variant = value;
|
||||||
|
} else {
|
||||||
|
variant = RawJson(value);
|
||||||
|
}
|
||||||
|
return JsonError::Ok;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename TReader, typename TWriter>
|
template <typename TReader, typename TWriter>
|
||||||
inline const char *
|
inline const char *
|
||||||
ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseString() {
|
ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseString() {
|
||||||
@ -159,18 +164,3 @@ ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseString() {
|
|||||||
|
|
||||||
return str.c_str();
|
return str.c_str();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename TReader, typename TWriter>
|
|
||||||
inline ArduinoJson::JsonError
|
|
||||||
ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseStringTo(
|
|
||||||
JsonVariant *destination) {
|
|
||||||
bool hasQuotes = isQuote(_reader.current());
|
|
||||||
const char *value = parseString();
|
|
||||||
if (value == NULL) return JsonError::NoMemory;
|
|
||||||
if (hasQuotes) {
|
|
||||||
*destination = value;
|
|
||||||
} else {
|
|
||||||
*destination = RawJson(value);
|
|
||||||
}
|
|
||||||
return JsonError::Ok;
|
|
||||||
}
|
|
||||||
|
@ -21,12 +21,20 @@ class JsonError {
|
|||||||
|
|
||||||
JsonError(Code code) : _code(code) {}
|
JsonError(Code code) : _code(code) {}
|
||||||
|
|
||||||
bool operator==(Code code) const {
|
friend bool operator==(const JsonError& err, Code code) {
|
||||||
return _code == code;
|
return err._code == code;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator!=(Code code) const {
|
friend bool operator==(Code code, const JsonError& err) {
|
||||||
return _code != code;
|
return err._code == code;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator!=(const JsonError& err, Code code) {
|
||||||
|
return err._code != code;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator!=(Code code, const JsonError& err) {
|
||||||
|
return err._code != code;
|
||||||
}
|
}
|
||||||
|
|
||||||
operator bool() const {
|
operator bool() const {
|
||||||
|
@ -5,45 +5,66 @@
|
|||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <catch.hpp>
|
#include <catch.hpp>
|
||||||
|
|
||||||
JsonError tryParseArray(const char *json, uint8_t nestingLimit) {
|
#define SHOULD_WORK(expression) REQUIRE(JsonError::Ok == expression);
|
||||||
DynamicJsonArray array;
|
#define SHOULD_FAIL(expression) REQUIRE(JsonError::TooDeep == expression);
|
||||||
return deserializeJson(array, json, nestingLimit);
|
|
||||||
}
|
|
||||||
|
|
||||||
JsonError tryParseObject(const char *json, uint8_t nestingLimit) {
|
|
||||||
DynamicJsonObject obj;
|
|
||||||
return deserializeJson(obj, json, nestingLimit);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_CASE("JsonParser nestingLimit") {
|
TEST_CASE("JsonParser nestingLimit") {
|
||||||
SECTION("ParseArrayWithNestingLimit0") {
|
SECTION("deserializeJson(JsonArray&)") {
|
||||||
REQUIRE(tryParseArray("[]", 0) == JsonError::Ok);
|
DynamicJsonArray arr;
|
||||||
REQUIRE(tryParseArray("[[]]", 0) == JsonError::TooDeep);
|
|
||||||
|
SECTION("limit = 0") {
|
||||||
|
SHOULD_FAIL(deserializeJson(arr, "[]", 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("ParseArrayWithNestingLimit1") {
|
SECTION("limit = 1") {
|
||||||
REQUIRE(tryParseArray("[[]]", 1) == JsonError::Ok);
|
SHOULD_WORK(deserializeJson(arr, "[]", 1));
|
||||||
REQUIRE(tryParseArray("[[[]]]", 1) == JsonError::TooDeep);
|
SHOULD_FAIL(deserializeJson(arr, "[[]]", 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("ParseArrayWithNestingLimit2") {
|
SECTION("limit = 2") {
|
||||||
REQUIRE(tryParseArray("[[[]]]", 2) == JsonError::Ok);
|
SHOULD_WORK(deserializeJson(arr, "[[]]", 2));
|
||||||
REQUIRE(tryParseArray("[[[[]]]]", 2) == JsonError::TooDeep);
|
SHOULD_FAIL(deserializeJson(arr, "[[[]]]", 2));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("ParseObjectWithNestingLimit0") {
|
SECTION("deserializeJson(JsonObject&)") {
|
||||||
REQUIRE(tryParseObject("{}", 0) == JsonError::Ok);
|
DynamicJsonObject obj;
|
||||||
REQUIRE(tryParseObject("{\"key\":{}}", 0) == JsonError::TooDeep);
|
|
||||||
|
SECTION("limit = 0") {
|
||||||
|
SHOULD_FAIL(deserializeJson(obj, "{}", 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("ParseObjectWithNestingLimit1") {
|
SECTION("limit = 1") {
|
||||||
REQUIRE(tryParseObject("{\"key\":{}}", 1) == JsonError::Ok);
|
SHOULD_WORK(deserializeJson(obj, "{\"key\":42}", 1));
|
||||||
REQUIRE(tryParseObject("{\"key\":{\"key\":{}}}", 1) == JsonError::TooDeep);
|
SHOULD_FAIL(deserializeJson(obj, "{\"key\":{\"key\":42}}", 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
SECTION("ParseObjectWithNestingLimit2") {
|
SECTION("limit = 2") {
|
||||||
REQUIRE(tryParseObject("{\"key\":{\"key\":{}}}", 2) == JsonError::Ok);
|
SHOULD_WORK(deserializeJson(obj, "{\"key\":{\"key\":42}}", 2));
|
||||||
REQUIRE(tryParseObject("{\"key\":{\"key\":{\"key\":{}}}}", 2) ==
|
SHOULD_FAIL(deserializeJson(obj, "{\"key\":{\"key\":{\"key\":42}}}", 2));
|
||||||
JsonError::TooDeep);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("deserializeJson(JsonVariant&)") {
|
||||||
|
DynamicJsonVariant var;
|
||||||
|
|
||||||
|
SECTION("limit = 0") {
|
||||||
|
SHOULD_WORK(deserializeJson(var, "\"toto\"", 0));
|
||||||
|
SHOULD_WORK(deserializeJson(var, "123", 0));
|
||||||
|
SHOULD_WORK(deserializeJson(var, "true", 0));
|
||||||
|
SHOULD_FAIL(deserializeJson(var, "[]", 0));
|
||||||
|
SHOULD_FAIL(deserializeJson(var, "{}", 0));
|
||||||
|
SHOULD_FAIL(deserializeJson(var, "[\"toto\"]", 0));
|
||||||
|
SHOULD_FAIL(deserializeJson(var, "{\"toto\":1}", 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
SECTION("limit = 1") {
|
||||||
|
SHOULD_WORK(deserializeJson(var, "[\"toto\"]", 1));
|
||||||
|
SHOULD_WORK(deserializeJson(var, "{\"toto\":1}", 1));
|
||||||
|
SHOULD_FAIL(deserializeJson(var, "{\"toto\":{}}", 1));
|
||||||
|
SHOULD_FAIL(deserializeJson(var, "{\"toto\":[]}", 1));
|
||||||
|
SHOULD_FAIL(deserializeJson(var, "[[\"toto\"]]", 1));
|
||||||
|
SHOULD_FAIL(deserializeJson(var, "[{\"toto\":1}]", 1));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user