diff --git a/extras/tests/JsonDeserializer/filter.cpp b/extras/tests/JsonDeserializer/filter.cpp index ed85838c..7296e886 100644 --- a/extras/tests/JsonDeserializer/filter.cpp +++ b/extras/tests/JsonDeserializer/filter.cpp @@ -2,6 +2,7 @@ // Copyright Benoit Blanchon 2014-2020 // MIT License +#define ARDUINOJSON_ENABLE_COMMENTS 1 #include #include @@ -257,11 +258,11 @@ TEST_CASE("Filtering") { JSON_ARRAY_SIZE(0) }, { - // ignore errors in skipped value + // detect errors in skipped value "[!,2,\\]", "[false]", 10, - DeserializationError::Ok, + DeserializationError::InvalidInput, "[]", JSON_ARRAY_SIZE(0) }, @@ -374,11 +375,11 @@ TEST_CASE("Filtering") { 0 }, { - // ignore invalid value in skipped object + // detect invalid value in skipped object "{'hello':!}", "false", 10, - DeserializationError::Ok, + DeserializationError::InvalidInput, "null", 0 }, @@ -387,7 +388,7 @@ TEST_CASE("Filtering") { "{'hello':\\}", "false", 10, - DeserializationError::Ok, + DeserializationError::InvalidInput, "null", 0 }, @@ -454,6 +455,168 @@ TEST_CASE("Filtering") { "null", 0 }, + { + // invalid comment at after an element in a skipped array + "[1/]", + "false", + 10, + DeserializationError::InvalidInput, + "null", + 0 + }, + { + // incomplete comment at after an element in a skipped array + "[1/*]", + "false", + 10, + DeserializationError::IncompleteInput, + "null", + 0 + }, + { + // missing comma in a skipped array + "[1 2]", + "false", + 10, + DeserializationError::InvalidInput, + "null", + 0 + }, + { + // invalid comment at the beginning of array + "[/1]", + "[false]", + 10, + DeserializationError::InvalidInput, + "[]", + JSON_ARRAY_SIZE(0) + }, + { + // incomplete comment at the begining of an array + "[/*]", + "[false]", + 10, + DeserializationError::IncompleteInput, + "[]", + JSON_ARRAY_SIZE(0) + }, + { + // invalid comment before key + "{/1:2}", + "{}", + 10, + DeserializationError::InvalidInput, + "{}", + JSON_OBJECT_SIZE(0) + }, + { + // incomplete comment before key + "{/*:2}", + "{}", + 10, + DeserializationError::IncompleteInput, + "{}", + JSON_OBJECT_SIZE(0) + }, + { + // invalid comment after key + "{\"example\"/1:2}", + "{}", + 10, + DeserializationError::InvalidInput, + "{}", + JSON_OBJECT_SIZE(0) + 8 + }, + { + // incomplete comment after key + "{\"example\"/*:2}", + "{}", + 10, + DeserializationError::IncompleteInput, + "{}", + JSON_OBJECT_SIZE(0) + 8 + }, + { + // invalid comment after colon + "{\"example\":/12}", + "{}", + 10, + DeserializationError::InvalidInput, + "{}", + JSON_OBJECT_SIZE(0) + }, + { + // incomplete comment after colon + "{\"example\":/*2}", + "{}", + 10, + DeserializationError::IncompleteInput, + "{}", + JSON_OBJECT_SIZE(0) + }, + { + // comment next to an integer + "{\"ignore\":1//,\"example\":2\n}", + "{\"example\":true}", + 10, + DeserializationError::Ok, + "{}", + JSON_OBJECT_SIZE(0) + }, + { + // invalid comment after opening brace of a skipped object + "{/1:2}", + "false", + 10, + DeserializationError::InvalidInput, + "null", + 0 + }, + { + // incomplete after opening brace of a skipped object + "{/*:2}", + "false", + 10, + DeserializationError::IncompleteInput, + "null", + 0 + }, + { + // invalid comment after key of a skipped object + "{\"example\"/:2}", + "false", + 10, + DeserializationError::InvalidInput, + "null", + 0 + }, + { + // incomplete after after key of a skipped object + "{\"example\"/*:2}", + "false", + 10, + DeserializationError::IncompleteInput, + "null", + 0 + }, + { + // invalid comment after value in a skipped object + "{\"example\":2/}", + "false", + 10, + DeserializationError::InvalidInput, + "null", + 0 + }, + { + // incomplete after after value of a skipped object + "{\"example\":2/*}", + "false", + 10, + DeserializationError::IncompleteInput, + "null", + 0 + }, }; // clang-format on for (size_t i = 0; i < sizeof(testCases) / sizeof(testCases[0]); i++) { diff --git a/extras/tests/JsonDeserializer/incomplete_input.cpp b/extras/tests/JsonDeserializer/incomplete_input.cpp index f66331fb..02661211 100644 --- a/extras/tests/JsonDeserializer/incomplete_input.cpp +++ b/extras/tests/JsonDeserializer/incomplete_input.cpp @@ -13,7 +13,9 @@ TEST_CASE("Truncated JSON input") { // true "t", "tr", "tru", // null - "n", "nu", "nul"}; + "n", "nu", "nul", + // object + "{", "{a", "{a:", "{a:1", "{a:1,", "{a:1,"}; const size_t testCount = sizeof(testCases) / sizeof(testCases[0]); DynamicJsonDocument doc(4096); diff --git a/extras/tests/JsonDeserializer/string.cpp b/extras/tests/JsonDeserializer/string.cpp index 40e4c7bb..7b5fdbfb 100644 --- a/extras/tests/JsonDeserializer/string.cpp +++ b/extras/tests/JsonDeserializer/string.cpp @@ -15,6 +15,14 @@ TEST_CASE("Valid JSON strings value") { TestCase testCases[] = { {"\"hello world\"", "hello world"}, {"\'hello world\'", "hello world"}, + {"'\"'", "\""}, + {"'\\\\'", "\\"}, + {"'\\/'", "/"}, + {"'\\b'", "\b"}, + {"'\\f'", "\f"}, + {"'\\n'", "\n"}, + {"'\\r'", "\r"}, + {"'\\t'", "\t"}, {"\"1\\\"2\\\\3\\/4\\b5\\f6\\n7\\r8\\t9\"", "1\"2\\3/4\b5\f6\n7\r8\t9"}, {"'\\u0041'", "A"}, {"'\\u00e4'", "\xc3\xa4"}, // รค @@ -33,8 +41,8 @@ TEST_CASE("Valid JSON strings value") { const TestCase& testCase = testCases[i]; CAPTURE(testCase.input); DeserializationError err = deserializeJson(doc, testCase.input); - REQUIRE(err == DeserializationError::Ok); - REQUIRE(doc.as() == testCase.expectedOutput); + CHECK(err == DeserializationError::Ok); + CHECK(doc.as() == testCase.expectedOutput); } } @@ -54,7 +62,7 @@ TEST_CASE("Truncated JSON string") { TEST_CASE("Invalid JSON string") { const char* testCases[] = {"'\\u'", "'\\u000g'", "'\\u000'", - "'\\u000G'", "'\\u000/'", "\\x1234"}; + "'\\u000G'", "'\\u000/'", "'\\x1234'"}; const size_t testCount = sizeof(testCases) / sizeof(testCases[0]); DynamicJsonDocument doc(4096); @@ -67,9 +75,15 @@ TEST_CASE("Invalid JSON string") { } TEST_CASE("Not enough room to duplicate the string") { - DynamicJsonDocument doc(4); + DynamicJsonDocument doc(JSON_OBJECT_SIZE(0)); - REQUIRE(deserializeJson(doc, "\"hello world!\"") == - DeserializationError::NoMemory); - REQUIRE(doc.isNull() == true); + SECTION("Quoted string") { + REQUIRE(deserializeJson(doc, "{\"example\":1}") == + DeserializationError::NoMemory); + } + + SECTION("Non-quoted string") { + REQUIRE(deserializeJson(doc, "{example:1}") == + DeserializationError::NoMemory); + } } diff --git a/extras/tests/JsonSerializer/JsonVariant.cpp b/extras/tests/JsonSerializer/JsonVariant.cpp index 13c1f397..0e14b5c9 100644 --- a/extras/tests/JsonSerializer/JsonVariant.cpp +++ b/extras/tests/JsonSerializer/JsonVariant.cpp @@ -31,6 +31,38 @@ TEST_CASE("serializeJson(JsonVariant)") { SECTION("string") { check(std::string("hello"), "\"hello\""); + + SECTION("Escape quotation mark") { + check(std::string("hello \"world\""), "\"hello \\\"world\\\"\""); + } + + SECTION("Escape reverse solidus") { + check(std::string("hello\\world"), "\"hello\\\\world\""); + } + + SECTION("Don't escape solidus") { + check(std::string("fifty/fifty"), "\"fifty/fifty\""); + } + + SECTION("Escape backspace") { + check(std::string("hello\bworld"), "\"hello\\bworld\""); + } + + SECTION("Escape formfeed") { + check(std::string("hello\fworld"), "\"hello\\fworld\""); + } + + SECTION("Escape linefeed") { + check(std::string("hello\nworld"), "\"hello\\nworld\""); + } + + SECTION("Escape carriage return") { + check(std::string("hello\rworld"), "\"hello\\rworld\""); + } + + SECTION("Escape tab") { + check(std::string("hello\tworld"), "\"hello\\tworld\""); + } } SECTION("SerializedValue") { diff --git a/src/ArduinoJson/Json/EscapeSequence.hpp b/src/ArduinoJson/Json/EscapeSequence.hpp index 90dee26c..d3925e70 100644 --- a/src/ArduinoJson/Json/EscapeSequence.hpp +++ b/src/ArduinoJson/Json/EscapeSequence.hpp @@ -12,7 +12,7 @@ class EscapeSequence { public: // Optimized for code size on a 8-bit AVR static char escapeChar(char c) { - const char *p = escapeTable(false); + const char *p = escapeTable(true); while (p[0] && p[1] != c) { p += 2; } @@ -21,10 +21,10 @@ class EscapeSequence { // Optimized for code size on a 8-bit AVR static char unescapeChar(char c) { - const char *p = escapeTable(true); + const char *p = escapeTable(false); for (;;) { if (p[0] == '\0') - return c; + return 0; if (p[0] == c) return p[1]; p += 2; @@ -32,8 +32,8 @@ class EscapeSequence { } private: - static const char *escapeTable(bool excludeIdenticals) { - return &"\"\"\\\\b\bf\fn\nr\rt\t"[excludeIdenticals ? 4 : 0]; + static const char *escapeTable(bool excludeSolidus) { + return &"//\"\"\\\\b\bf\fn\nr\rt\t"[excludeSolidus ? 2 : 0]; } }; } // namespace ARDUINOJSON_NAMESPACE diff --git a/src/ArduinoJson/Json/JsonDeserializer.hpp b/src/ArduinoJson/Json/JsonDeserializer.hpp index e4e31e04..d27810b9 100644 --- a/src/ArduinoJson/Json/JsonDeserializer.hpp +++ b/src/ArduinoJson/Json/JsonDeserializer.hpp @@ -299,7 +299,9 @@ class JsonDeserializer { // Skip spaces err = skipSpacesAndComments(); if (err) - return err; // Colon + return err; + + // Colon if (!eat(':')) return DeserializationError::InvalidInput; @@ -393,8 +395,7 @@ class JsonDeserializer { StringBuilder builder = _stringStorage.startString(); char c = current(); - if (c == '\0') - return DeserializationError::IncompleteInput; + ARDUINOJSON_ASSERT(c); if (canBeInNonQuotedString(c)) { // no quotes do { @@ -482,7 +483,7 @@ class JsonDeserializer { DeserializationError skipNumericValue() { char c = current(); - while (c && c != '}' && c != ',' && c != ']' && c != ':') { + while (canBeInNonQuotedString(c)) { move(); c = current(); }