Skip to content

Commit

Permalink
Added optional support for trailing commas
Browse files Browse the repository at this point in the history
This adds kParseTrailingCommasFlag to allow a trailing comma at the
end of maps and arrays. This is part of issue Tencent#36, adding optional
support for relaxed JSON syntax.
  • Loading branch information
ludocode committed Mar 20, 2016
1 parent ca07fe2 commit 3e21bb4
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 0 deletions.
1 change: 1 addition & 0 deletions doc/dom.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ Parse flags | Meaning
`kParseStopWhenDoneFlag` | After parsing a complete JSON root from stream, stop further processing the rest of stream. When this flag is used, parser will not generate `kParseErrorDocumentRootNotSingular` error. Using this flag for parsing multiple JSONs in the same stream.
`kParseFullPrecisionFlag` | Parse number in full precision (slower). If this flag is not set, the normal precision (faster) is used. Normal precision has maximum 3 [ULP](http://en.wikipedia.org/wiki/Unit_in_the_last_place) error.
`kParseCommentsFlag` | Allow one-line `// ...` and multi-line `/* ... */` comments (relaxed JSON syntax).
`kParseTrailingCommasFlag` | Allow trailing commas at the end of objects and arrays (relaxed JSON syntax).
By using a non-type template parameter, instead of a function parameter, C++ compiler can generate code which is optimized for specified combinations, improving speed, and reducing code size (if only using a single specialization). The downside is the flags needed to be determined in compile-time.
Expand Down
19 changes: 19 additions & 0 deletions include/rapidjson/reader.h
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ enum ParseFlag {
kParseFullPrecisionFlag = 16, //!< Parse number in full precision (but slower).
kParseCommentsFlag = 32, //!< Allow one-line (//) and multi-line (/**/) comments.
kParseNumbersAsStringsFlag = 64, //!< Parse all numbers (ints/doubles) as strings.
kParseTrailingCommasFlag = 128, //!< Allow trailing commas at the end of objects and arrays.
kParseDefaultFlags = RAPIDJSON_PARSE_DEFAULT_FLAGS //!< Default parse flags. Can be customized by defining RAPIDJSON_PARSE_DEFAULT_FLAGS
};

Expand Down Expand Up @@ -636,6 +637,15 @@ class GenericReader {
RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell());
break;
}

if (parseFlags & kParseTrailingCommasFlag) {
if (is.Peek() == '}') {
is.Take();
if (RAPIDJSON_UNLIKELY(!handler.EndObject(memberCount)))
RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
return;
}
}
}
}

Expand Down Expand Up @@ -676,6 +686,15 @@ class GenericReader {
}
else
RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell());

if (parseFlags & kParseTrailingCommasFlag) {
if (is.Peek() == ']') {
is.Take();
if (RAPIDJSON_UNLIKELY(!handler.EndArray(elementCount)))
RAPIDJSON_PARSE_ERROR(kParseErrorTermination, is.Tell());
return;
}
}
}
}

Expand Down
37 changes: 37 additions & 0 deletions test/unittest/readertest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,10 @@ TEST(Reader, ParseArray_Error) {
TEST_ARRAY_ERROR(kParseErrorArrayMissCommaOrSquareBracket, "[1}", 2);
TEST_ARRAY_ERROR(kParseErrorArrayMissCommaOrSquareBracket, "[1 2]", 3);

// Array cannot have a trailing comma (without kParseTrailingCommasFlag);
// a value must follow a comma
TEST_ARRAY_ERROR(kParseErrorValueInvalid, "[1,]", 3);

#undef TEST_ARRAY_ERROR
}

Expand Down Expand Up @@ -978,6 +982,10 @@ TEST(Reader, ParseObject_Error) {
// Must be a comma or '}' after an object member
TEST_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, "{\"a\":1]", 6);

// Object cannot have a trailing comma (without kParseTrailingCommasFlag);
// an object member name must follow a comma
TEST_ERROR(kParseErrorObjectMissName, "{\"a\":1,}", 7);

// This tests that MemoryStream is checking the length in Peek().
{
MemoryStream ms("{\"a\"", 1);
Expand Down Expand Up @@ -1552,6 +1560,35 @@ TEST(Reader, NumbersAsStrings) {
}
}

TEST(Reader, TrailingCommas) {
{
// trailing array comma
StringStream s("[1,2,3,]");
ParseArrayHandler<3> h;
Reader reader;
EXPECT_TRUE(reader.Parse<kParseTrailingCommasFlag>(s, h));
EXPECT_EQ(5u, h.step_);
}
{
// trailing object comma
const char* json = "{ \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3],}";
StringStream s(json);
ParseObjectHandler h;
Reader reader;
EXPECT_TRUE(reader.Parse<kParseTrailingCommasFlag>(s, h));
EXPECT_EQ(20u, h.step_);
}
{
// trailing object and array commas with whitespace
const char* json = "{ \"hello\" : \"world\", \"t\" : true , \"f\" : false, \"n\": null, \"i\":123, \"pi\": 3.1416, \"a\":[1, 2, 3\n,\n]\n,\n } ";
StringStream s(json);
ParseObjectHandler h;
Reader reader;
EXPECT_TRUE(reader.Parse<kParseTrailingCommasFlag>(s, h));
EXPECT_EQ(20u, h.step_);
}
}

#ifdef __GNUC__
RAPIDJSON_DIAG_POP
#endif
Expand Down

0 comments on commit 3e21bb4

Please sign in to comment.