Disabled lazy number deserialization (fixes #772)

This commit is contained in:
Benoit Blanchon
2018-07-04 11:57:30 +02:00
parent 1397bec066
commit 037f90aada
16 changed files with 251 additions and 161 deletions

View File

@ -1,6 +1,16 @@
ArduinoJson: change log
=======================
HEAD
----
* Disabled lazy number deserialization (issue #772)
> ### BREAKING CHANGES
>
> Non quoted strings are now forbidden in values, but they are still allowed in keys.
> For example, `{key:"value"}` is accepted, but `{key:value}` is not.
v6.1.0-beta
-----------
@ -15,7 +25,7 @@ v6.1.0-beta
> JsonObject& obj = doc.to<JsonObject>();
> JsonArray& arr = obj.createNestedArray("key");
> if (!arr.success()) {
> Serial.println("No enough memory");
> Serial.println("Not enough memory");
> return;
> }
> ```
@ -26,7 +36,7 @@ v6.1.0-beta
> JsonObject obj = doc.to<JsonObject>();
> JsonArray arr = obj.createNestedArray("key");
> if (arr.isNull()) {
> Serial.println("No enough memory");
> Serial.println("Not enough memory");
> return;
> }
> ```

View File

@ -121,7 +121,7 @@ class JsonDeserializer {
for (;;) {
// Parse key
const char *key;
err = parseString(&key);
err = parseKey(&key);
if (err) return err;
// Skip spaces
@ -152,30 +152,38 @@ class JsonDeserializer {
}
DeserializationError parseValue(JsonVariant &variant) {
bool hasQuotes = isQuote(current());
const char *value;
DeserializationError error = parseString(&value);
if (error) return error;
if (hasQuotes) {
variant = value;
if (isQuote(current())) {
return parseStringValue(variant);
} else {
variant = RawJson(value);
return parseNumericValue(variant);
}
}
DeserializationError parseKey(const char **key) {
if (isQuote(current())) {
return parseQuotedString(key);
} else {
return parseNonQuotedString(key);
}
}
DeserializationError parseStringValue(JsonVariant &variant) {
const char *value;
DeserializationError err = parseQuotedString(&value);
if (err) return err;
variant = value;
return DeserializationError::Ok;
}
DeserializationError parseString(const char **result) {
DeserializationError parseQuotedString(const char **result) {
typename remove_reference<TStringStorage>::type::String str =
_stringStorage.startString();
char c = current();
if (c == '\0') return DeserializationError::IncompleteInput;
char stopChar = current();
if (isQuote(c)) { // quotes
move();
char stopChar = c;
for (;;) {
c = current();
char c = current();
move();
if (c == stopChar) break;
@ -193,7 +201,20 @@ class JsonDeserializer {
str.append(c);
}
} else if (canBeInNonQuotedString(c)) { // no quotes
*result = str.c_str();
if (*result == NULL) return DeserializationError::NoMemory;
return DeserializationError::Ok;
}
DeserializationError parseNonQuotedString(const char **result) {
typename remove_reference<TStringStorage>::type::String str =
_stringStorage.startString();
char c = current();
if (c == '\0') return DeserializationError::IncompleteInput;
if (canBeInNonQuotedString(c)) { // no quotes
do {
move();
str.append(c);
@ -208,6 +229,34 @@ class JsonDeserializer {
return DeserializationError::Ok;
}
DeserializationError parseNumericValue(JsonVariant &result) {
char buffer[64];
uint8_t n = 0;
char c = current();
while (canBeInNonQuotedString(c) && n < 63) {
move();
buffer[n++] = c;
c = current();
}
buffer[n] = 0;
if (isInteger(buffer)) {
result = parseInteger<JsonInteger>(buffer);
} else if (isFloat(buffer)) {
result = parseFloat<JsonFloat>(buffer);
} else if (!strcmp(buffer, "true")) {
result = true;
} else if (!strcmp(buffer, "false")) {
result = false;
} else if (!strcmp(buffer, "null")) {
result = static_cast<const char *>(0);
} else {
return DeserializationError::InvalidInput;
}
return DeserializationError::Ok;
}
static inline bool isBetween(char c, char min, char max) {
return min <= c && c <= max;
}
@ -286,7 +335,7 @@ class JsonDeserializer {
uint8_t _nestingLimit;
char _current;
bool _loaded;
};
}; // namespace Internals
} // namespace Internals
template <typename TDocument, typename TInput>

View File

@ -10,7 +10,7 @@ namespace ArduinoJson {
namespace Internals {
inline bool isInteger(const char* s) {
if (!s) return false;
if (!s || !*s) return false;
if (issign(*s)) s++;
while (isdigit(*s)) s++;
return *s == '\0';

View File

@ -67,13 +67,13 @@ endif()
add_subdirectory(DynamicJsonBuffer)
add_subdirectory(IntegrationTests)
add_subdirectory(JsonArray)
add_subdirectory(JsonObject)
add_subdirectory(JsonDeserializer)
add_subdirectory(JsonObject)
add_subdirectory(JsonSerializer)
add_subdirectory(JsonVariant)
add_subdirectory(JsonWriter)
add_subdirectory(Misc)
add_subdirectory(MsgPackDeserializer)
add_subdirectory(MsgPackSerializer)
add_subdirectory(Polyfills)
add_subdirectory(Numbers)
add_subdirectory(StaticJsonBuffer)

View File

@ -4,6 +4,7 @@
add_executable(IntegrationTests
gbathree.cpp
issue772.cpp
round_trip.cpp
)

View File

@ -0,0 +1,27 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson.h>
#include <catch.hpp>
// https://github.com/bblanchon/ArduinoJson/issues/772
TEST_CASE("Issue772") {
DynamicJsonDocument doc1, doc2;
DeserializationError err;
std::string data =
"{\"state\":{\"reported\":{\"timestamp\":\"2018-07-02T09:40:12Z\","
"\"mac\":\"2C3AE84FC076\",\"firmwareVersion\":\"v0.2.7-5-gf4d4d78\","
"\"visibleLight\":261,\"infraRed\":255,\"ultraViolet\":0.02,"
"\"Temperature\":26.63,\"Pressure\":101145.7,\"Humidity\":54.79883,"
"\"Vbat\":4.171261,\"soilMoisture\":0,\"ActB\":0}}}";
err = deserializeJson(doc1, data);
REQUIRE(err == DeserializationError::Ok);
data = "";
serializeMsgPack(doc1, data);
err = deserializeMsgPack(doc2, data);
REQUIRE(err == DeserializationError::Ok);
}

View File

@ -128,12 +128,7 @@ TEST_CASE("deserialize JSON array") {
SECTION("No quotes") {
DeserializationError err = deserializeJson(doc, "[ hello , world ]");
JsonArray arr = doc.as<JsonArray>();
REQUIRE(err == DeserializationError::Ok);
REQUIRE(2 == arr.size());
REQUIRE(arr[0] == "hello");
REQUIRE(arr[1] == "world");
REQUIRE(err == DeserializationError::InvalidInput);
}
SECTION("Double quotes (empty strings)") {

View File

@ -39,7 +39,7 @@ TEST_CASE("deserialize JSON object") {
}
SECTION("No quotes") {
DeserializationError err = deserializeJson(doc, "{key:value}");
DeserializationError err = deserializeJson(doc, "{key:'value'}");
JsonObject obj = doc.as<JsonObject>();
REQUIRE(err == DeserializationError::Ok);

View File

@ -21,7 +21,7 @@ TEST_CASE("deserializeJson(std::istream&)") {
}
SECTION("object") {
std::istringstream json(" { hello : world // comment\n }");
std::istringstream json(" { hello : 'world' // comment\n }");
DeserializationError err = deserializeJson(doc, json);
JsonObject obj = doc.as<JsonObject>();

View File

@ -2,12 +2,12 @@
# Copyright Benoit Blanchon 2014-2018
# MIT License
add_executable(PolyfillsTests
add_executable(NumbersTests
isFloat.cpp
isInteger.cpp
parseFloat.cpp
parseInteger.cpp
)
target_link_libraries(PolyfillsTests catch)
add_test(Polyfills PolyfillsTests)
target_link_libraries(NumbersTests catch)
add_test(Numbers NumbersTests)

80
test/Numbers/isFloat.cpp Normal file
View File

@ -0,0 +1,80 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson/Numbers/isFloat.hpp>
#include <catch.hpp>
using namespace ArduinoJson::Internals;
TEST_CASE("isFloat()") {
SECTION("Input is NULL") {
REQUIRE(isFloat(NULL) == false);
}
SECTION("Empty string") {
REQUIRE(isFloat("") == false);
}
SECTION("NoExponent") {
REQUIRE(isFloat("3.14") == true);
REQUIRE(isFloat("-3.14") == true);
REQUIRE(isFloat("+3.14") == true);
}
SECTION("IntegralPartMissing") {
REQUIRE(isFloat(".14") == true);
REQUIRE(isFloat("-.14") == true);
REQUIRE(isFloat("+.14") == true);
}
SECTION("FractionalPartMissing") {
REQUIRE(isFloat("3.") == true);
REQUIRE(isFloat("-3.e14") == true);
REQUIRE(isFloat("+3.e-14") == true);
}
SECTION("NoDot") {
REQUIRE(isFloat("3e14") == true);
REQUIRE(isFloat("3e-14") == true);
REQUIRE(isFloat("3e+14") == true);
}
SECTION("Integer") {
REQUIRE(isFloat("14") == true);
REQUIRE(isFloat("-14") == true);
REQUIRE(isFloat("+14") == true);
}
SECTION("ExponentMissing") {
REQUIRE(isFloat("3.14e") == false);
REQUIRE(isFloat("3.14e-") == false);
REQUIRE(isFloat("3.14e+") == false);
}
SECTION("JustASign") {
REQUIRE(isFloat("-") == false);
REQUIRE(isFloat("+") == false);
}
SECTION("Empty") {
REQUIRE(isFloat("") == false);
}
SECTION("NaN") {
REQUIRE(isFloat("NaN") == true);
REQUIRE(isFloat("n") == false);
REQUIRE(isFloat("N") == false);
REQUIRE(isFloat("nan") == false);
REQUIRE(isFloat("-NaN") == false);
REQUIRE(isFloat("+NaN") == false);
}
SECTION("Infinity") {
REQUIRE(isFloat("Infinity") == true);
REQUIRE(isFloat("+Infinity") == true);
REQUIRE(isFloat("-Infinity") == true);
REQUIRE(isFloat("infinity") == false);
REQUIRE(isFloat("Inf") == false);
}
}

View File

@ -0,0 +1,40 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson/Numbers/isInteger.hpp>
#include <catch.hpp>
using namespace ArduinoJson::Internals;
TEST_CASE("isInteger()") {
SECTION("Null") {
REQUIRE(isInteger(NULL) == false);
}
SECTION("Empty string") {
REQUIRE(isInteger("") == false);
}
SECTION("FloatNotInteger") {
REQUIRE(isInteger("3.14") == false);
REQUIRE(isInteger("-3.14") == false);
REQUIRE(isInteger("+3.14") == false);
}
SECTION("Spaces") {
REQUIRE(isInteger("42 ") == false);
REQUIRE(isInteger(" 42") == false);
}
SECTION("Valid") {
REQUIRE(isInteger("42") == true);
REQUIRE(isInteger("-42") == true);
REQUIRE(isInteger("+42") == true);
}
SECTION("ExtraSign") {
REQUIRE(isInteger("--42") == false);
REQUIRE(isInteger("++42") == false);
}
}

View File

@ -1,76 +0,0 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson/Numbers/isFloat.hpp>
#include <catch.hpp>
using namespace ArduinoJson::Internals;
TEST_CASE("isFloat()") {
SECTION("Input is NULL") {
REQUIRE(isFloat(NULL) == false);
}
SECTION("NoExponent") {
REQUIRE(isFloat("3.14"));
REQUIRE(isFloat("-3.14"));
REQUIRE(isFloat("+3.14"));
}
SECTION("IntegralPartMissing") {
REQUIRE(isFloat(".14"));
REQUIRE(isFloat("-.14"));
REQUIRE(isFloat("+.14"));
}
SECTION("FractionalPartMissing") {
REQUIRE(isFloat("3."));
REQUIRE(isFloat("-3.e14"));
REQUIRE(isFloat("+3.e-14"));
}
SECTION("NoDot") {
REQUIRE(isFloat("3e14"));
REQUIRE(isFloat("3e-14"));
REQUIRE(isFloat("3e+14"));
}
SECTION("Integer") {
REQUIRE(isFloat("14"));
REQUIRE(isFloat("-14"));
REQUIRE(isFloat("+14"));
}
SECTION("ExponentMissing") {
REQUIRE_FALSE(isFloat("3.14e"));
REQUIRE_FALSE(isFloat("3.14e-"));
REQUIRE_FALSE(isFloat("3.14e+"));
}
SECTION("JustASign") {
REQUIRE_FALSE(isFloat("-"));
REQUIRE_FALSE(isFloat("+"));
}
SECTION("Empty") {
REQUIRE_FALSE(isFloat(""));
}
SECTION("NaN") {
REQUIRE(isFloat("NaN"));
REQUIRE_FALSE(isFloat("n"));
REQUIRE_FALSE(isFloat("N"));
REQUIRE_FALSE(isFloat("nan"));
REQUIRE_FALSE(isFloat("-NaN"));
REQUIRE_FALSE(isFloat("+NaN"));
}
SECTION("Infinity") {
REQUIRE(isFloat("Infinity"));
REQUIRE(isFloat("+Infinity"));
REQUIRE(isFloat("-Infinity"));
REQUIRE_FALSE(isFloat("infinity"));
REQUIRE_FALSE(isFloat("Inf"));
}
}

View File

@ -1,36 +0,0 @@
// ArduinoJson - arduinojson.org
// Copyright Benoit Blanchon 2014-2018
// MIT License
#include <ArduinoJson/Numbers/isInteger.hpp>
#include <catch.hpp>
using namespace ArduinoJson::Internals;
TEST_CASE("isInteger()") {
SECTION("Null") {
REQUIRE_FALSE(isInteger(NULL));
}
SECTION("FloatNotInteger") {
REQUIRE_FALSE(isInteger("3.14"));
REQUIRE_FALSE(isInteger("-3.14"));
REQUIRE_FALSE(isInteger("+3.14"));
}
SECTION("Spaces") {
REQUIRE_FALSE(isInteger("42 "));
REQUIRE_FALSE(isInteger(" 42"));
}
SECTION("Valid") {
REQUIRE(isInteger("42"));
REQUIRE(isInteger("-42"));
REQUIRE(isInteger("+42"));
}
SECTION("ExtraSign") {
REQUIRE_FALSE(isInteger("--42"));
REQUIRE_FALSE(isInteger("++42"));
}
}