瀏覽代碼

Disabled lazy number deserialization (fixes #772)

Benoit Blanchon 7 年之前
父節點
當前提交
037f90aada

+ 12 - 2
CHANGELOG.md

@@ -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;
 > }
 > ```

+ 78 - 29
src/ArduinoJson/Json/JsonDeserializer.hpp

@@ -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,48 +152,69 @@ 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 {
+      return parseNumericValue(variant);
+    }
+  }
+
+  DeserializationError parseKey(const char **key) {
+    if (isQuote(current())) {
+      return parseQuotedString(key);
     } else {
-      variant = RawJson(value);
+      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();
+    for (;;) {
+      char c = current();
       move();
-      char stopChar = c;
-      for (;;) {
+      if (c == stopChar) break;
+
+      if (c == '\0') return DeserializationError::IncompleteInput;
+
+      if (c == '\\') {
         c = current();
+        if (c == '\0') return DeserializationError::IncompleteInput;
+        if (c == 'u') return DeserializationError::NotSupported;
+        // replace char
+        c = EscapeSequence::unescapeChar(c);
+        if (c == '\0') return DeserializationError::InvalidInput;
         move();
-        if (c == stopChar) break;
+      }
 
-        if (c == '\0') return DeserializationError::IncompleteInput;
+      str.append(c);
+    }
 
-        if (c == '\\') {
-          c = current();
-          if (c == '\0') return DeserializationError::IncompleteInput;
-          if (c == 'u') return DeserializationError::NotSupported;
-          // replace char
-          c = EscapeSequence::unescapeChar(c);
-          if (c == '\0') return DeserializationError::InvalidInput;
-          move();
-        }
+    *result = str.c_str();
+    if (*result == NULL) return DeserializationError::NoMemory;
+    return DeserializationError::Ok;
+  }
 
-        str.append(c);
-      }
-    } else if (canBeInNonQuotedString(c)) {  // no quotes
+  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>

+ 1 - 1
src/ArduinoJson/Numbers/isInteger.hpp

@@ -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';

+ 2 - 2
test/CMakeLists.txt

@@ -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)

+ 1 - 0
test/IntegrationTests/CMakeLists.txt

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

+ 27 - 0
test/IntegrationTests/issue772.cpp

@@ -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);
+}

+ 1 - 6
test/JsonDeserializer/deserializeJsonArray.cpp

@@ -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)") {

+ 1 - 1
test/JsonDeserializer/deserializeJsonObject.cpp

@@ -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);

+ 1 - 1
test/JsonDeserializer/std_istream.cpp

@@ -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>();

+ 3 - 3
test/Polyfills/CMakeLists.txt → test/Numbers/CMakeLists.txt

@@ -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 - 0
test/Numbers/isFloat.cpp

@@ -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);
+  }
+}

+ 40 - 0
test/Numbers/isInteger.cpp

@@ -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);
+  }
+}

+ 0 - 0
test/Polyfills/parseFloat.cpp → test/Numbers/parseFloat.cpp


+ 0 - 0
test/Polyfills/parseInteger.cpp → test/Numbers/parseInteger.cpp


+ 0 - 76
test/Polyfills/isFloat.cpp

@@ -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"));
-  }
-}

+ 0 - 36
test/Polyfills/isInteger.cpp

@@ -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"));
-  }
-}