Эх сурвалжийг харах

Allowed non-quoted key to contain underscores (fixes #665)

Benoit Blanchon 8 жил өмнө
parent
commit
d9b1e7e810

+ 1 - 0
CHANGELOG.md

@@ -5,6 +5,7 @@ HEAD
 ----
 
 * Fixed `JsonVariant::operator|(int)` that returned the default value if the variant contained a double (issue #675)
+* Allowed non-quoted key to contain underscores (issue #665)
 
 v5.13.0
 -------

+ 6 - 6
src/ArduinoJson/Deserialization/JsonParser.hpp

@@ -50,13 +50,13 @@ class JsonParser {
   inline bool parseObjectTo(JsonVariant *destination);
   inline bool parseStringTo(JsonVariant *destination);
 
-  static inline bool isInRange(char c, char min, char max) {
+  static inline bool isBetween(char c, char min, char max) {
     return min <= c && c <= max;
   }
 
-  static inline bool isLetterOrNumber(char c) {
-    return isInRange(c, '0', '9') || isInRange(c, 'a', 'z') ||
-           isInRange(c, 'A', 'Z') || c == '+' || c == '-' || c == '.';
+  static inline bool canBeInNonQuotedString(char c) {
+    return isBetween(c, '0', '9') || isBetween(c, '_', 'z') ||
+           isBetween(c, 'A', 'Z') || c == '+' || c == '-' || c == '.';
   }
 
   static inline bool isQuote(char c) {
@@ -99,5 +99,5 @@ inline typename JsonParserBuilder<TJsonBuffer, TString>::TParser makeParser(
   return JsonParserBuilder<TJsonBuffer, TString>::makeParser(buffer, json,
                                                              nestingLimit);
 }
-}
-}
+}  // namespace Internals
+}  // namespace ArduinoJson

+ 1 - 1
src/ArduinoJson/Deserialization/JsonParserImpl.hpp

@@ -167,7 +167,7 @@ ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseString() {
     }
   } else {  // no quotes
     for (;;) {
-      if (!isLetterOrNumber(c)) break;
+      if (!canBeInNonQuotedString(c)) break;
       _reader.move();
       str.append(c);
       c = _reader.current();

+ 153 - 138
test/JsonBuffer/parseObject.cpp

@@ -8,148 +8,163 @@
 TEST_CASE("JsonBuffer::parseObject()") {
   DynamicJsonBuffer jb;
 
-  SECTION("EmptyObject") {
+  SECTION("An empty object") {
     JsonObject& obj = jb.parseObject("{}");
     REQUIRE(obj.success());
     REQUIRE(obj.size() == 0);
   }
 
-  SECTION("MissingOpeningBrace") {
-    JsonObject& obj = jb.parseObject("}");
-    REQUIRE_FALSE(obj.success());
-  }
-
-  SECTION("MissingClosingBrace") {
-    JsonObject& obj = jb.parseObject("{");
-    REQUIRE_FALSE(obj.success());
-  }
-
-  SECTION("MissingColonAndValue") {
-    JsonObject& obj = jb.parseObject("{\"key\"}");
-    REQUIRE_FALSE(obj.success());
-  }
-
-  SECTION("MissingQuotesAndColonAndValue") {
-    JsonObject& obj = jb.parseObject("{key}");
-    REQUIRE_FALSE(obj.success());
-  }
-
-  SECTION("OneString") {
-    JsonObject& obj = jb.parseObject("{\"key\":\"value\"}");
-    REQUIRE(obj.success());
-    REQUIRE(obj.size() == 1);
-    REQUIRE(obj["key"] == "value");
-  }
-
-  SECTION("OneStringSingleQuotes") {
-    JsonObject& obj = jb.parseObject("{'key':'value'}");
-    REQUIRE(obj.success());
-    REQUIRE(obj.size() == 1);
-    REQUIRE(obj["key"] == "value");
-  }
-
-  SECTION("OneStringNoQuotes") {
-    JsonObject& obj = jb.parseObject("{key:value}");
-    REQUIRE(obj.success());
-    REQUIRE(obj.size() == 1);
-    REQUIRE(obj["key"] == "value");
-  }
-
-  SECTION("OneStringSpaceBeforeKey") {
-    JsonObject& obj = jb.parseObject("{ \"key\":\"value\"}");
-    REQUIRE(obj.success());
-    REQUIRE(obj.size() == 1);
-    REQUIRE(obj["key"] == "value");
-  }
-
-  SECTION("OneStringSpaceAfterKey") {
-    JsonObject& obj = jb.parseObject("{\"key\" :\"value\"}");
-    REQUIRE(obj.success());
-    REQUIRE(obj.size() == 1);
-    REQUIRE(obj["key"] == "value");
-  }
-
-  SECTION("OneStringSpaceBeforeValue") {
-    JsonObject& obj = jb.parseObject("{\"key\": \"value\"}");
-    REQUIRE(obj.success());
-    REQUIRE(obj.size() == 1);
-    REQUIRE(obj["key"] == "value");
-  }
-
-  SECTION("OneStringSpaceAfterValue") {
-    JsonObject& obj = jb.parseObject("{\"key\":\"value\" }");
-    REQUIRE(obj.success());
-    REQUIRE(obj.size() == 1);
-    REQUIRE(obj["key"] == "value");
-  }
-
-  SECTION("TwoStrings") {
-    JsonObject& obj =
-        jb.parseObject("{\"key1\":\"value1\",\"key2\":\"value2\"}");
-    REQUIRE(obj.success());
-    REQUIRE(obj.size() == 2);
-    REQUIRE(obj["key1"] == "value1");
-    REQUIRE(obj["key2"] == "value2");
-  }
-
-  SECTION("TwoStringsSpaceBeforeComma") {
-    JsonObject& obj =
-        jb.parseObject("{\"key1\":\"value1\" ,\"key2\":\"value2\"}");
-    REQUIRE(obj.success());
-    REQUIRE(obj.size() == 2);
-    REQUIRE(obj["key1"] == "value1");
-    REQUIRE(obj["key2"] == "value2");
-  }
-
-  SECTION("TwoStringsSpaceAfterComma") {
-    JsonObject& obj =
-        jb.parseObject("{\"key1\":\"value1\" ,\"key2\":\"value2\"}");
-    REQUIRE(obj.success());
-    REQUIRE(obj.size() == 2);
-    REQUIRE(obj["key1"] == "value1");
-    REQUIRE(obj["key2"] == "value2");
-  }
-
-  SECTION("EndingWithAComma") {
-    JsonObject& obj = jb.parseObject("{\"key1\":\"value1\",}");
-    REQUIRE_FALSE(obj.success());
-    REQUIRE(obj.size() == 0);
-  }
-
-  SECTION("TwoIntergers") {
-    JsonObject& obj = jb.parseObject("{\"key1\":42,\"key2\":-42}");
-    REQUIRE(obj.success());
-    REQUIRE(obj.size() == 2);
-    REQUIRE(obj["key1"] == 42);
-    REQUIRE(obj["key2"] == -42);
-  }
-
-  SECTION("TwoDoubles") {
-    JsonObject& obj = jb.parseObject("{\"key1\":12.345,\"key2\":-7E89}");
-    REQUIRE(obj.success());
-    REQUIRE(obj.size() == 2);
-    REQUIRE(obj["key1"] == 12.345);
-    REQUIRE(obj["key2"] == -7E89);
-  }
-
-  SECTION("TwoBooleans") {
-    JsonObject& obj = jb.parseObject("{\"key1\":true,\"key2\":false}");
-    REQUIRE(obj.success());
-    REQUIRE(obj.size() == 2);
-    REQUIRE(obj["key1"] == true);
-    REQUIRE(obj["key2"] == false);
-  }
-
-  SECTION("TwoNulls") {
-    JsonObject& obj = jb.parseObject("{\"key1\":null,\"key2\":null}");
-    REQUIRE(obj.success());
-    REQUIRE(obj.size() == 2);
-    REQUIRE(obj["key1"].as<char*>() == 0);
-    REQUIRE(obj["key2"].as<char*>() == 0);
-  }
-
-  SECTION("NullForKey") {
-    JsonObject& obj = jb.parseObject("null:\"value\"}");
-    REQUIRE_FALSE(obj.success());
+  SECTION("Quotes") {
+    SECTION("Double quotes") {
+      JsonObject& obj = jb.parseObject("{\"key\":\"value\"}");
+      REQUIRE(obj.success());
+      REQUIRE(obj.size() == 1);
+      REQUIRE(obj["key"] == "value");
+    }
+
+    SECTION("Single quotes") {
+      JsonObject& obj = jb.parseObject("{'key':'value'}");
+      REQUIRE(obj.success());
+      REQUIRE(obj.size() == 1);
+      REQUIRE(obj["key"] == "value");
+    }
+
+    SECTION("No quotes") {
+      JsonObject& obj = jb.parseObject("{key:value}");
+      REQUIRE(obj.success());
+      REQUIRE(obj.size() == 1);
+      REQUIRE(obj["key"] == "value");
+    }
+
+    SECTION("No quotes, allow underscore in key") {
+      JsonObject& obj = jb.parseObject("{_k_e_y_:42}");
+      REQUIRE(obj.success());
+      REQUIRE(obj.size() == 1);
+      REQUIRE(obj["_k_e_y_"] == 42);
+    }
+  }
+
+  SECTION("Spaces") {
+    SECTION("Before the key") {
+      JsonObject& obj = jb.parseObject("{ \"key\":\"value\"}");
+      REQUIRE(obj.success());
+      REQUIRE(obj.size() == 1);
+      REQUIRE(obj["key"] == "value");
+    }
+
+    SECTION("After the key") {
+      JsonObject& obj = jb.parseObject("{\"key\" :\"value\"}");
+      REQUIRE(obj.success());
+      REQUIRE(obj.size() == 1);
+      REQUIRE(obj["key"] == "value");
+    }
+
+    SECTION("Before the value") {
+      JsonObject& obj = jb.parseObject("{\"key\": \"value\"}");
+      REQUIRE(obj.success());
+      REQUIRE(obj.size() == 1);
+      REQUIRE(obj["key"] == "value");
+    }
+
+    SECTION("After the value") {
+      JsonObject& obj = jb.parseObject("{\"key\":\"value\" }");
+      REQUIRE(obj.success());
+      REQUIRE(obj.size() == 1);
+      REQUIRE(obj["key"] == "value");
+    }
+
+    SECTION("Before the colon") {
+      JsonObject& obj =
+          jb.parseObject("{\"key1\":\"value1\" ,\"key2\":\"value2\"}");
+      REQUIRE(obj.success());
+      REQUIRE(obj.size() == 2);
+      REQUIRE(obj["key1"] == "value1");
+      REQUIRE(obj["key2"] == "value2");
+    }
+
+    SECTION("After the colon") {
+      JsonObject& obj =
+          jb.parseObject("{\"key1\":\"value1\" ,\"key2\":\"value2\"}");
+      REQUIRE(obj.success());
+      REQUIRE(obj.size() == 2);
+      REQUIRE(obj["key1"] == "value1");
+      REQUIRE(obj["key2"] == "value2");
+    }
+  }
+
+  SECTION("Values types") {
+    SECTION("String") {
+      JsonObject& obj =
+          jb.parseObject("{\"key1\":\"value1\",\"key2\":\"value2\"}");
+      REQUIRE(obj.success());
+      REQUIRE(obj.size() == 2);
+      REQUIRE(obj["key1"] == "value1");
+      REQUIRE(obj["key2"] == "value2");
+    }
+
+    SECTION("Integer") {
+      JsonObject& obj = jb.parseObject("{\"key1\":42,\"key2\":-42}");
+      REQUIRE(obj.success());
+      REQUIRE(obj.size() == 2);
+      REQUIRE(obj["key1"] == 42);
+      REQUIRE(obj["key2"] == -42);
+    }
+
+    SECTION("Double") {
+      JsonObject& obj = jb.parseObject("{\"key1\":12.345,\"key2\":-7E89}");
+      REQUIRE(obj.success());
+      REQUIRE(obj.size() == 2);
+      REQUIRE(obj["key1"] == 12.345);
+      REQUIRE(obj["key2"] == -7E89);
+    }
+
+    SECTION("Booleans") {
+      JsonObject& obj = jb.parseObject("{\"key1\":true,\"key2\":false}");
+      REQUIRE(obj.success());
+      REQUIRE(obj.size() == 2);
+      REQUIRE(obj["key1"] == true);
+      REQUIRE(obj["key2"] == false);
+    }
+
+    SECTION("Null") {
+      JsonObject& obj = jb.parseObject("{\"key1\":null,\"key2\":null}");
+      REQUIRE(obj.success());
+      REQUIRE(obj.size() == 2);
+      REQUIRE(obj["key1"].as<char*>() == 0);
+      REQUIRE(obj["key2"].as<char*>() == 0);
+    }
+  }
+
+  SECTION("Misc") {
+    SECTION("The opening brace is missing") {
+      JsonObject& obj = jb.parseObject("}");
+      REQUIRE_FALSE(obj.success());
+    }
+
+    SECTION("The closing brace is missing") {
+      JsonObject& obj = jb.parseObject("{");
+      REQUIRE_FALSE(obj.success());
+    }
+
+    SECTION("A quoted key without value") {
+      JsonObject& obj = jb.parseObject("{\"key\"}");
+      REQUIRE_FALSE(obj.success());
+    }
+
+    SECTION("A non-quoted key without value") {
+      JsonObject& obj = jb.parseObject("{key}");
+      REQUIRE_FALSE(obj.success());
+    }
+
+    SECTION("A dangling comma") {
+      JsonObject& obj = jb.parseObject("{\"key1\":\"value1\",}");
+      REQUIRE_FALSE(obj.success());
+      REQUIRE(obj.size() == 0);
+    }
+
+    SECTION("null as a key") {
+      JsonObject& obj = jb.parseObject("null:\"value\"}");
+      REQUIRE_FALSE(obj.success());
+    }
   }
 }