Commit 143bb83e authored by Milo Yip's avatar Milo Yip

Merge pull request #101 from pah/feature/rfc7159

Move to RFC7159 (closes #90)
parents 12da5ecb 68630336
......@@ -42,7 +42,7 @@ The JSON is now parsed into `document` as a *DOM tree*:
![DOM in the tutorial](diagram/tutorial.png)
The root of a conforming JSON should be either an object or an array. In this case, the root is an object.
Since the update to RFC7159, the root of a conforming JSON document can be any JSON value. In RFC4627, only objects or arrays were allowed as root values. In this case, the root is an object.
~~~~~~~~~~cpp
assert(document.IsObject());
~~~~~~~~~~
......
......@@ -37,7 +37,6 @@ inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErro
case kParseErrorNone: return RAPIDJSON_ERROR_STRING("No error.");
case kParseErrorDocumentEmpty: return RAPIDJSON_ERROR_STRING("The document is empty.");
case kParseErrorDocumentRootNotObjectOrArray: return RAPIDJSON_ERROR_STRING("The document root must be either object or array.");
case kParseErrorDocumentRootNotSingular: return RAPIDJSON_ERROR_STRING("The document root must not follow by other values.");
case kParseErrorValueInvalid: return RAPIDJSON_ERROR_STRING("Invalid value.");
......
......@@ -57,7 +57,6 @@ enum ParseErrorCode {
kParseErrorNone = 0, //!< No error.
kParseErrorDocumentEmpty, //!< The document is empty.
kParseErrorDocumentRootNotObjectOrArray, //!< The document root must be either object or array.
kParseErrorDocumentRootNotSingular, //!< The document root must not follow by other values.
kParseErrorValueInvalid, //!< Invalid value.
......
......@@ -21,6 +21,11 @@
#ifndef RAPIDJSON_INTERNAL_META_H_
#define RAPIDJSON_INTERNAL_META_H_
#ifdef __GNUC__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++)
#endif
//@cond RAPIDJSON_INTERNAL
namespace rapidjson {
namespace internal {
......@@ -94,4 +99,8 @@ template <typename T> struct RemoveSfinaeFptr<SfinaeResultTag&(*)(T)> { typedef
} // namespace rapidjson
//@endcond
#ifdef __GNUC__
RAPIDJSON_DIAG_POP
#endif
#endif // RAPIDJSON_INTERNAL_META_H_
......@@ -174,7 +174,6 @@ protected:
level->valueCount++;
}
else {
RAPIDJSON_ASSERT(type == kObjectType || type == kArrayType);
RAPIDJSON_ASSERT(!Base::hasRoot_); // Should only has one and only one root.
Base::hasRoot_ = true;
}
......
......@@ -26,6 +26,7 @@
#include "rapidjson.h"
#include "encodings.h"
#include "internal/meta.h"
#include "internal/pow10.h"
#include "internal/stack.h"
......@@ -122,23 +123,25 @@ concept Handler {
/*! This can be used as base class of any reader handler.
\note implements Handler concept
*/
template<typename Encoding = UTF8<> >
template<typename Encoding = UTF8<>, typename Derived = void>
struct BaseReaderHandler {
typedef typename Encoding::Ch Ch;
typedef typename internal::SelectIf<internal::IsSame<Derived, void>, BaseReaderHandler, Derived>::Type Override;
bool Default() { return true; }
bool Null() { return Default(); }
bool Bool(bool) { return Default(); }
bool Int(int) { return Default(); }
bool Uint(unsigned) { return Default(); }
bool Int64(int64_t) { return Default(); }
bool Uint64(uint64_t) { return Default(); }
bool Double(double) { return Default(); }
bool String(const Ch*, SizeType, bool) { return Default(); }
bool StartObject() { return Default(); }
bool EndObject(SizeType) { return Default(); }
bool StartArray() { return Default(); }
bool EndArray(SizeType) { return Default(); }
bool Null() { return static_cast<Override&>(*this).Default(); }
bool Bool(bool) { return static_cast<Override&>(*this).Default(); }
bool Int(int) { return static_cast<Override&>(*this).Default(); }
bool Uint(unsigned) { return static_cast<Override&>(*this).Default(); }
bool Int64(int64_t) { return static_cast<Override&>(*this).Default(); }
bool Uint64(uint64_t) { return static_cast<Override&>(*this).Default(); }
bool Double(double) { return static_cast<Override&>(*this).Default(); }
bool String(const Ch*, SizeType, bool) { return static_cast<Override&>(*this).Default(); }
bool StartObject() { return static_cast<Override&>(*this).Default(); }
bool EndObject(SizeType) { return static_cast<Override&>(*this).Default(); }
bool StartArray() { return static_cast<Override&>(*this).Default(); }
bool EndArray(SizeType) { return static_cast<Override&>(*this).Default(); }
};
///////////////////////////////////////////////////////////////////////////////
......@@ -381,11 +384,7 @@ public:
RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_);
}
else {
switch (is.Peek()) {
case '{': ParseObject<parseFlags>(is, handler); break;
case '[': ParseArray<parseFlags>(is, handler); break;
default: RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotObjectOrArray, is.Tell());
}
ParseValue<parseFlags>(is, handler);
RAPIDJSON_PARSE_ERROR_EARLY_RETURN(parseResult_);
if (!(parseFlags & kParseStopWhenDoneFlag)) {
......@@ -907,6 +906,9 @@ private:
IterativeParsingElementDelimiterState,
IterativeParsingArrayFinishState,
// Single value state
IterativeParsingValueState,
cIterativeParsingStateCount
};
......@@ -965,11 +967,11 @@ private:
IterativeParsingErrorState, // Right curly bracket
IterativeParsingErrorState, // Comma
IterativeParsingErrorState, // Colon
IterativeParsingErrorState, // String
IterativeParsingErrorState, // False
IterativeParsingErrorState, // True
IterativeParsingErrorState, // Null
IterativeParsingErrorState // Number
IterativeParsingValueState, // String
IterativeParsingValueState, // False
IterativeParsingValueState, // True
IterativeParsingValueState, // Null
IterativeParsingValueState // Number
},
// Finish(sink state)
{
......@@ -1102,6 +1104,12 @@ private:
IterativeParsingElementState // Number
},
// ArrayFinish(sink state)
{
IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState,
IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState,
IterativeParsingErrorState
},
// Single Value (sink state)
{
IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState,
IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState, IterativeParsingErrorState,
......@@ -1242,6 +1250,14 @@ private:
}
}
case IterativeParsingValueState:
// Must be non-compound value. Or it would be ObjectInitial or ArrayInitial state.
ParseValue<parseFlags>(is, handler);
if (HasParseError()) {
return IterativeParsingErrorState;
}
return IterativeParsingFinishState;
default:
RAPIDJSON_ASSERT(false);
return IterativeParsingErrorState;
......@@ -1256,7 +1272,7 @@ private:
}
switch (src) {
case IterativeParsingStartState: RAPIDJSON_PARSE_ERROR(is.Peek() == '\0' ? kParseErrorDocumentEmpty : kParseErrorDocumentRootNotObjectOrArray, is.Tell());
case IterativeParsingStartState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentEmpty, is.Tell());
case IterativeParsingFinishState: RAPIDJSON_PARSE_ERROR(kParseErrorDocumentRootNotSingular, is.Tell());
case IterativeParsingObjectInitialState:
case IterativeParsingMemberDelimiterState: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell());
......
......@@ -321,7 +321,6 @@ protected:
level->valueCount++;
}
else {
RAPIDJSON_ASSERT(type == kObjectType || type == kArrayType);
RAPIDJSON_ASSERT(!hasRoot_); // Should only has one and only one root.
hasRoot_ = true;
}
......
......@@ -22,9 +22,9 @@ RapidJSON is a JSON parser and generator for C++. It was inspired by [RapidXml](
More features can be read [here](doc/features.md).
JSON(JavaScript Object Notation) is a light-weight data exchange format. RapidJSON should be in fully compliance with RFC4627/ECMA-404. More information about JSON can be obtained at
JSON(JavaScript Object Notation) is a light-weight data exchange format. RapidJSON should be in fully compliance with RFC7159/ECMA-404. More information about JSON can be obtained at
* [Introducing JSON](http://json.org/)
* [RFC4627: The application/json Media Type for JavaScript Object Notation (JSON)](http://www.ietf.org/rfc/rfc4627.txt)
* [RFC7159: The JavaScript Object Notation (JSON) Data Interchange Format](http://www.ietf.org/rfc/rfc7159.txt)
* [Standard ECMA-404: The JSON Data Interchange Format](http://www.ecma-international.org/publications/standards/Ecma-404.htm)
## Compatibility
......
......@@ -46,6 +46,8 @@ TEST(JsonChecker, Reader) {
// jsonchecker/failXX.json
for (int i = 1; i <= 33; i++) {
if (i == 1) // fail1.json is valid in rapidjson, which has no limitation on type of root element (RFC 7159).
continue;
if (i == 18) // fail18.json is valid in rapidjson, which has no limitation on depth of nesting.
continue;
......@@ -57,14 +59,18 @@ TEST(JsonChecker, Reader) {
json = ReadFile(filename, length);
if (!json) {
printf("jsonchecker file %s not found", filename);
ADD_FAILURE();
continue;
}
}
GenericDocument<UTF8<>, CrtAllocator> document; // Use Crt allocator to check exception-safety (no memory leak)
if (!document.Parse((const char*)json).HasParseError())
FAIL();
//printf("%s(%u):%s\n", filename, (unsigned)document.GetErrorOffset(), document.GetParseError());
document.Parse((const char*)json);
EXPECT_TRUE(document.HasParseError());
document.Parse<kParseIterativeFlag>((const char*)json);
EXPECT_TRUE(document.HasParseError());
free(json);
}
......@@ -84,7 +90,11 @@ TEST(JsonChecker, Reader) {
GenericDocument<UTF8<>, CrtAllocator> document; // Use Crt allocator to check exception-safety (no memory leak)
document.Parse((const char*)json);
EXPECT_TRUE(!document.HasParseError());
EXPECT_FALSE(document.HasParseError());
document.Parse<kParseIterativeFlag>((const char*)json);
EXPECT_FALSE(document.HasParseError());
free(json);
}
}
This diff is collapsed.
......@@ -50,6 +50,16 @@ TEST(Writer, Compact) {
EXPECT_TRUE(writer.IsComplete()); \
}
TEST(Writer, Root) {
TEST_ROUNDTRIP("null");
TEST_ROUNDTRIP("true");
TEST_ROUNDTRIP("false");
TEST_ROUNDTRIP("0");
TEST_ROUNDTRIP("\"foo\"");
TEST_ROUNDTRIP("[]");
TEST_ROUNDTRIP("{}");
}
TEST(Writer, Int) {
TEST_ROUNDTRIP("[-1]");
TEST_ROUNDTRIP("[-123]");
......@@ -155,12 +165,12 @@ TEST(Writer, OStreamWrapper) {
EXPECT_STREQ("{\"hello\":\"world\",\"t\":true,\"f\":false,\"n\":null,\"i\":123,\"pi\":3.1416,\"a\":[1,2,3]}", actual.c_str());
}
TEST(Writer, AssertRootMustBeArrayOrObject) {
TEST(Writer, AssertRootMayBeAnyValue) {
#define T(x)\
{\
StringBuffer buffer;\
Writer<StringBuffer> writer(buffer);\
ASSERT_THROW(x, AssertException);\
EXPECT_TRUE(x);\
}
T(writer.Bool(false));
T(writer.Bool(true));
......@@ -228,9 +238,23 @@ TEST(Writer, AssertObjectKeyNotString) {
TEST(Writer, AssertMultipleRoot) {
StringBuffer buffer;
Writer<StringBuffer> writer(buffer);
writer.StartObject();
writer.EndObject();
ASSERT_THROW(writer.StartObject(), AssertException);
writer.Reset(buffer);
writer.Null();
ASSERT_THROW(writer.Int(0), AssertException);
writer.Reset(buffer);
writer.String("foo");
ASSERT_THROW(writer.StartArray(), AssertException);
writer.Reset(buffer);
writer.StartArray();
writer.EndArray();
ASSERT_THROW(writer.Double(3.14), AssertException);
}
TEST(Writer, RootObjectIsComplete) {
......@@ -260,3 +284,24 @@ TEST(Writer, RootArrayIsComplete) {
writer.EndArray();
EXPECT_TRUE(writer.IsComplete());
}
TEST(Writer, RootValueIsComplete) {
#define T(x)\
{\
StringBuffer buffer;\
Writer<StringBuffer> writer(buffer);\
EXPECT_FALSE(writer.IsComplete()); \
x; \
EXPECT_TRUE(writer.IsComplete()); \
}
T(writer.Null());
T(writer.Bool(true));
T(writer.Bool(false));
T(writer.Int(0));
T(writer.Uint(0));
T(writer.Int64(0));
T(writer.Uint64(0));
T(writer.Double(0));
T(writer.String(""));
#undef T
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment