Added support of comments in JSON input (issue #88)

This commit is contained in:
Benoit Blanchon
2015-07-27 22:16:42 +02:00
parent c161f698fc
commit e31d667bec
8 changed files with 242 additions and 37 deletions

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
*.sh text eol=lf

View File

@ -8,6 +8,7 @@ v5.0 (currently in beta)
* Added `JsonBuffer::strdup()` to make a copy of a string (issues #10, #57) * Added `JsonBuffer::strdup()` to make a copy of a string (issues #10, #57)
* Implicitly call `strdup()` for `String` but not for `char*` (issues #84, #87) * Implicitly call `strdup()` for `String` but not for `char*` (issues #84, #87)
* Added support of non standard JSON input (issue #44) * Added support of non standard JSON input (issue #44)
* Added support of comments in JSON input (issue #88)
* Redesigned `JsonVariant` to leverage converting constructors instead of assignment operators (issue #66) * Redesigned `JsonVariant` to leverage converting constructors instead of assignment operators (issue #66)
* Switched to new the library layout (requires Arduino 1.0.6 or above) * Switched to new the library layout (requires Arduino 1.0.6 or above)

View File

@ -0,0 +1,13 @@
// Copyright Benoit Blanchon 2014-2015
// MIT License
//
// Arduino JSON library
// https://github.com/bblanchon/ArduinoJson
#pragma once
namespace ArduinoJson {
namespace Internals {
const char *skipSpacesAndComments(const char *ptr);
}
}

View File

@ -1,16 +1,15 @@
#!/bin/bash #!/bin/bash
ZIP="C:\Program Files\7-Zip\7z.exe"
TAG=$(git describe) TAG=$(git describe)
OUTPUT="ArduinoJson-$TAG.zip" OUTPUT="ArduinoJson-$TAG.zip"
cd ../.. cd $(dirname $0)/../..
# remove existing file # remove existing file
rm -f $OUTPUT rm -f $OUTPUT
# create zip # create zip
"$ZIP" a $OUTPUT \ 7z a $OUTPUT \
ArduinoJson/CHANGELOG.md \ ArduinoJson/CHANGELOG.md \
ArduinoJson/examples \ ArduinoJson/examples \
ArduinoJson/include \ ArduinoJson/include \

View File

@ -11,6 +11,12 @@
#include <math.h> // for isnan() and isinf() #include <math.h> // for isnan() and isinf()
#include <stdio.h> // for sprintf() #include <stdio.h> // for sprintf()
// only for GCC 4.9+
#if defined(__GNUC__) && \
(__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9))
#pragma GCC diagnostic ignored "-Wfloat-conversion"
#endif
size_t Print::print(const char s[]) { size_t Print::print(const char s[]) {
size_t n = 0; size_t n = 0;
while (*s) { while (*s) {

View File

@ -0,0 +1,51 @@
// Copyright Benoit Blanchon 2014-2015
// MIT License
//
// Arduino JSON library
// https://github.com/bblanchon/ArduinoJson
#include "../../include/ArduinoJson/Internals/Comments.hpp"
inline static const char *skipCStyleComment(const char *ptr) {
ptr += 2;
for (;;) {
if (ptr[0] == '\0') return ptr;
if (ptr[0] == '*' && ptr[1] == '/') return ptr + 2;
ptr++;
}
}
inline static const char *skipCppStyleComment(const char *ptr) {
ptr += 2;
for (;;) {
if (ptr[0] == '\0' || ptr[0] == '\n') return ptr;
ptr++;
}
}
const char *ArduinoJson::Internals::skipSpacesAndComments(const char *ptr) {
for (;;) {
switch (ptr[0]) {
case ' ':
case '\t':
case '\r':
case '\n':
ptr++;
continue;
case '/':
switch (ptr[1]) {
case '*':
ptr = skipCStyleComment(ptr);
break;
case '/':
ptr = skipCppStyleComment(ptr);
break;
default:
return ptr;
}
break;
default:
return ptr;
}
}
}

View File

@ -9,6 +9,7 @@
#include <stdlib.h> // for strtol, strtod #include <stdlib.h> // for strtol, strtod
#include <ctype.h> #include <ctype.h>
#include "../../include/ArduinoJson/Internals/Comments.hpp"
#include "../../include/ArduinoJson/Internals/Encoding.hpp" #include "../../include/ArduinoJson/Internals/Encoding.hpp"
#include "../../include/ArduinoJson/JsonArray.hpp" #include "../../include/ArduinoJson/JsonArray.hpp"
#include "../../include/ArduinoJson/JsonBuffer.hpp" #include "../../include/ArduinoJson/JsonBuffer.hpp"
@ -17,16 +18,11 @@
using namespace ArduinoJson; using namespace ArduinoJson;
using namespace ArduinoJson::Internals; using namespace ArduinoJson::Internals;
static const char *skipSpaces(const char *ptr) {
while (isspace(*ptr)) ptr++;
return ptr;
}
bool JsonParser::skip(char charToSkip) { bool JsonParser::skip(char charToSkip) {
register const char *ptr = skipSpaces(_readPtr); register const char *ptr = skipSpacesAndComments(_readPtr);
if (*ptr != charToSkip) return false; if (*ptr != charToSkip) return false;
ptr++; ptr++;
_readPtr = skipSpaces(ptr); _readPtr = skipSpacesAndComments(ptr);
return true; return true;
} }
@ -50,7 +46,7 @@ bool JsonParser::parseAnythingTo(JsonVariant *destination) {
} }
inline bool JsonParser::parseAnythingToUnsafe(JsonVariant *destination) { inline bool JsonParser::parseAnythingToUnsafe(JsonVariant *destination) {
_readPtr = skipSpaces(_readPtr); _readPtr = skipSpacesAndComments(_readPtr);
switch (*_readPtr) { switch (*_readPtr) {
case '[': case '[':
@ -212,8 +208,13 @@ bool JsonParser::parseNullTo(JsonVariant *destination) {
return true; return true;
} }
static bool isStopChar(char c) { static inline bool isInRange(char c, char min, char max) {
return c == '\0' || c == ':' || c == '}' || c == ']' || c == ','; return min <= c && c <= max;
}
static inline bool isLetterOrNumber(char c) {
return isInRange(c, '0', '9') || isInRange(c, 'a', 'z') ||
isInRange(c, 'A', 'Z');
} }
const char *JsonParser::parseString() { const char *JsonParser::parseString() {
@ -222,7 +223,7 @@ const char *JsonParser::parseString() {
char c = *readPtr; char c = *readPtr;
if (c == '\'' || c == '\"') { if (c == '\'' || c == '\"') { // quotes
char stopChar = c; char stopChar = c;
for (;;) { for (;;) {
c = *++readPtr; c = *++readPtr;
@ -241,9 +242,9 @@ const char *JsonParser::parseString() {
*writePtr++ = c; *writePtr++ = c;
} }
} else { } else { // no quotes
for (;;) { for (;;) {
if (isStopChar(c)) break; if (!isLetterOrNumber(c)) break;
*writePtr++ = c; *writePtr++ = c;
c = *++readPtr; c = *++readPtr;
} }

View File

@ -164,8 +164,26 @@ TEST_F(JsonParser_Array_Tests, MixedTrueFalse) {
parseMustFail(); parseMustFail();
} }
TEST_F(JsonParser_Array_Tests, TwoStrings) { TEST_F(JsonParser_Array_Tests, TwoStringsDoubleQuotes) {
whenInputIs("[\"hello\",\"world\"]"); whenInputIs("[ \"hello\" , \"world\" ]");
parseMustSucceed();
sizeMustBe(2);
firstElementMustBe("hello");
secondElementMustBe("world");
}
TEST_F(JsonParser_Array_Tests, TwoStringsSingleQuotes) {
whenInputIs("[ 'hello' , 'world' ]");
parseMustSucceed();
sizeMustBe(2);
firstElementMustBe("hello");
secondElementMustBe("world");
}
TEST_F(JsonParser_Array_Tests, TwoStringsNoQuotes) {
whenInputIs("[ hello , world ]");
parseMustSucceed(); parseMustSucceed();
sizeMustBe(2); sizeMustBe(2);
@ -224,3 +242,118 @@ TEST_F(JsonParser_Array_Tests, StringWithUnterminatedEscapeSequence) {
whenInputIs("\"\\\0\"", 4); whenInputIs("\"\\\0\"", 4);
parseMustFail(); parseMustFail();
} }
TEST_F(JsonParser_Array_Tests, CCommentBeforeOpeningBracket) {
whenInputIs("/*COMMENT*/[\"hello\"]");
parseMustSucceed();
sizeMustBe(1);
firstElementMustBe("hello");
}
TEST_F(JsonParser_Array_Tests, CCommentAfterOpeningBracket) {
whenInputIs("[/*COMMENT*/\"hello\"]");
parseMustSucceed();
sizeMustBe(1);
firstElementMustBe("hello");
}
TEST_F(JsonParser_Array_Tests, CCommentBeforeClosingBracket) {
whenInputIs("[\"hello\"/*COMMENT*/]");
parseMustSucceed();
sizeMustBe(1);
firstElementMustBe("hello");
}
TEST_F(JsonParser_Array_Tests, CCommentAfterClosingBracket) {
whenInputIs("[\"hello\"]/*COMMENT*/");
parseMustSucceed();
sizeMustBe(1);
firstElementMustBe("hello");
}
TEST_F(JsonParser_Array_Tests, CCommentBeforeComma) {
whenInputIs("[\"hello\"/*COMMENT*/,\"world\"]");
parseMustSucceed();
sizeMustBe(2);
firstElementMustBe("hello");
secondElementMustBe("world");
}
TEST_F(JsonParser_Array_Tests, CCommentAfterComma) {
whenInputIs("[\"hello\",/*COMMENT*/\"world\"]");
parseMustSucceed();
sizeMustBe(2);
firstElementMustBe("hello");
secondElementMustBe("world");
}
TEST_F(JsonParser_Array_Tests, CppCommentBeforeOpeningBracket) {
whenInputIs("//COMMENT\n[\"hello\"]");
parseMustSucceed();
sizeMustBe(1);
firstElementMustBe("hello");
}
TEST_F(JsonParser_Array_Tests, CppCommentAfterOpeningBracket) {
whenInputIs("[//COMMENT\n\"hello\"]");
parseMustSucceed();
sizeMustBe(1);
firstElementMustBe("hello");
}
TEST_F(JsonParser_Array_Tests, CppCommentBeforeClosingBracket) {
whenInputIs("[\"hello\"//COMMENT\n]");
parseMustSucceed();
sizeMustBe(1);
firstElementMustBe("hello");
}
TEST_F(JsonParser_Array_Tests, CppCommentAfterClosingBracket) {
whenInputIs("[\"hello\"]//COMMENT\n");
parseMustSucceed();
sizeMustBe(1);
firstElementMustBe("hello");
}
TEST_F(JsonParser_Array_Tests, CppCommentBeforeComma) {
whenInputIs("[\"hello\"//COMMENT\n,\"world\"]");
parseMustSucceed();
sizeMustBe(2);
firstElementMustBe("hello");
secondElementMustBe("world");
}
TEST_F(JsonParser_Array_Tests, CppCommentAfterComma) {
whenInputIs("[\"hello\",//COMMENT\n\"world\"]");
parseMustSucceed();
sizeMustBe(2);
firstElementMustBe("hello");
secondElementMustBe("world");
}
TEST_F(JsonParser_Array_Tests, InvalidCppComment) {
whenInputIs("[/COMMENT\n]");
parseMustFail();
}
TEST_F(JsonParser_Array_Tests, InvalidComment) {
whenInputIs("[/*/\n]");
parseMustFail();
}
TEST_F(JsonParser_Array_Tests, UnfinishedCComment) {
whenInputIs("[/*COMMENT]");
parseMustFail();
}