Skip to content

Commit

Permalink
Added support of comments in JSON input (issue #88)
Browse files Browse the repository at this point in the history
  • Loading branch information
bblanchon committed Jul 27, 2015
1 parent c161f69 commit e31d667
Show file tree
Hide file tree
Showing 8 changed files with 242 additions and 37 deletions.
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.sh text eol=lf
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ v5.0 (currently in beta)
* 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)
* 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)
* Switched to new the library layout (requires Arduino 1.0.6 or above)

Expand Down
13 changes: 13 additions & 0 deletions include/ArduinoJson/Internals/Comments.hpp
Original file line number Diff line number Diff line change
@@ -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);
}
}
43 changes: 21 additions & 22 deletions scripts/build-arduino-package.sh
Original file line number Diff line number Diff line change
@@ -1,22 +1,21 @@
#!/bin/bash

ZIP="C:\Program Files\7-Zip\7z.exe"
TAG=$(git describe)
OUTPUT="ArduinoJson-$TAG.zip"

cd ../..

# remove existing file
rm -f $OUTPUT

# create zip
"$ZIP" a $OUTPUT \
ArduinoJson/CHANGELOG.md \
ArduinoJson/examples \
ArduinoJson/include \
ArduinoJson/keywords.txt \
ArduinoJson/library.properties \
ArduinoJson/LICENSE.md \
ArduinoJson/README.md \
ArduinoJson/src \
-x!ArduinoJson/src/CMakeLists.txt
#!/bin/bash

TAG=$(git describe)
OUTPUT="ArduinoJson-$TAG.zip"

cd $(dirname $0)/../..

# remove existing file
rm -f $OUTPUT

# create zip
7z a $OUTPUT \
ArduinoJson/CHANGELOG.md \
ArduinoJson/examples \
ArduinoJson/include \
ArduinoJson/keywords.txt \
ArduinoJson/library.properties \
ArduinoJson/LICENSE.md \
ArduinoJson/README.md \
ArduinoJson/src \
-x!ArduinoJson/src/CMakeLists.txt
6 changes: 6 additions & 0 deletions src/Arduino/Print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@
#include <math.h> // for isnan() and isinf()
#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 n = 0;
while (*s) {
Expand Down
51 changes: 51 additions & 0 deletions src/Internals/Comments.cpp
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
27 changes: 14 additions & 13 deletions src/Internals/JsonParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <stdlib.h> // for strtol, strtod
#include <ctype.h>

#include "../../include/ArduinoJson/Internals/Comments.hpp"
#include "../../include/ArduinoJson/Internals/Encoding.hpp"
#include "../../include/ArduinoJson/JsonArray.hpp"
#include "../../include/ArduinoJson/JsonBuffer.hpp"
Expand All @@ -17,16 +18,11 @@
using namespace ArduinoJson;
using namespace ArduinoJson::Internals;

static const char *skipSpaces(const char *ptr) {
while (isspace(*ptr)) ptr++;
return ptr;
}

bool JsonParser::skip(char charToSkip) {
register const char *ptr = skipSpaces(_readPtr);
register const char *ptr = skipSpacesAndComments(_readPtr);
if (*ptr != charToSkip) return false;
ptr++;
_readPtr = skipSpaces(ptr);
_readPtr = skipSpacesAndComments(ptr);
return true;
}

Expand All @@ -50,7 +46,7 @@ bool JsonParser::parseAnythingTo(JsonVariant *destination) {
}

inline bool JsonParser::parseAnythingToUnsafe(JsonVariant *destination) {
_readPtr = skipSpaces(_readPtr);
_readPtr = skipSpacesAndComments(_readPtr);

switch (*_readPtr) {
case '[':
Expand Down Expand Up @@ -212,8 +208,13 @@ bool JsonParser::parseNullTo(JsonVariant *destination) {
return true;
}

static bool isStopChar(char c) {
return c == '\0' || c == ':' || c == '}' || c == ']' || c == ',';
static inline bool isInRange(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');
}

const char *JsonParser::parseString() {
Expand All @@ -222,7 +223,7 @@ const char *JsonParser::parseString() {

char c = *readPtr;

if (c == '\'' || c == '\"') {
if (c == '\'' || c == '\"') { // quotes
char stopChar = c;
for (;;) {
c = *++readPtr;
Expand All @@ -241,9 +242,9 @@ const char *JsonParser::parseString() {

*writePtr++ = c;
}
} else {
} else { // no quotes
for (;;) {
if (isStopChar(c)) break;
if (!isLetterOrNumber(c)) break;
*writePtr++ = c;
c = *++readPtr;
}
Expand Down
137 changes: 135 additions & 2 deletions test/JsonParser_Array_Tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,26 @@ TEST_F(JsonParser_Array_Tests, MixedTrueFalse) {
parseMustFail();
}

TEST_F(JsonParser_Array_Tests, TwoStrings) {
whenInputIs("[\"hello\",\"world\"]");
TEST_F(JsonParser_Array_Tests, TwoStringsDoubleQuotes) {
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();
sizeMustBe(2);
Expand Down Expand Up @@ -224,3 +242,118 @@ TEST_F(JsonParser_Array_Tests, StringWithUnterminatedEscapeSequence) {
whenInputIs("\"\\\0\"", 4);
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();
}

0 comments on commit e31d667

Please sign in to comment.