diff --git a/example/condense/condense.cpp b/example/condense/condense.cpp index 8f1da83..ae6d36a 100644 --- a/example/condense/condense.cpp +++ b/example/condense/condense.cpp @@ -7,6 +7,7 @@ #include "rapidjson/writer.h" #include "rapidjson/filereadstream.h" #include "rapidjson/filewritestream.h" +#include "rapidjson/error/en.h" using namespace rapidjson; @@ -23,7 +24,7 @@ int main(int, char*[]) { // JSON reader parse from the input stream and let writer generate the output. if (!reader.Parse<0>(is, writer)) { - fprintf(stderr, "\nError(%u): %s\n", (unsigned)reader.GetErrorOffset(), reader.GetParseError()); + fprintf(stderr, "\nError(%u): %s\n", (unsigned)reader.GetErrorOffset(), GetParseError_En(reader.GetParseErrorCode())); return 1; } diff --git a/example/pretty/pretty.cpp b/example/pretty/pretty.cpp index b09fc78..cfb3f0f 100644 --- a/example/pretty/pretty.cpp +++ b/example/pretty/pretty.cpp @@ -5,6 +5,7 @@ #include "rapidjson/prettywriter.h" #include "rapidjson/filereadstream.h" #include "rapidjson/filewritestream.h" +#include "rapidjson/error/en.h" using namespace rapidjson; @@ -21,7 +22,7 @@ int main(int, char*[]) { // JSON reader parse from the input stream and let writer generate the output. if (!reader.Parse(is, writer)) { - fprintf(stderr, "\nError(%u): %s\n", (unsigned)reader.GetErrorOffset(), reader.GetParseError()); + fprintf(stderr, "\nError(%u): %s\n", (unsigned)reader.GetErrorOffset(), GetParseError_En(reader.GetParseErrorCode())); return 1; } diff --git a/example/prettyauto/prettyauto.cpp b/example/prettyauto/prettyauto.cpp index 2e52e9e..3f85f40 100644 --- a/example/prettyauto/prettyauto.cpp +++ b/example/prettyauto/prettyauto.cpp @@ -7,6 +7,7 @@ #include "rapidjson/filereadstream.h" #include "rapidjson/filewritestream.h" #include "rapidjson/encodedstream.h" // NEW +#include "rapidjson/error/en.h" #ifdef _WIN32 #include #include @@ -47,7 +48,7 @@ int main(int, char*[]) { // JSON reader parse from the input stream and let writer generate the output. //if (!reader.Parse(is, writer)) { if (!reader.Parse(eis, writer)) { // CHANGED - fprintf(stderr, "\nError(%u): %s\n", (unsigned)reader.GetErrorOffset(), reader.GetParseError()); + fprintf(stderr, "\nError(%u): %s\n", (unsigned)reader.GetErrorOffset(), GetParseError_En(reader.GetParseErrorCode())); return 1; } diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 40a0442..a5e718f 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -788,7 +788,7 @@ public: /*! \param allocator Optional allocator for allocating stack memory. \param stackCapacity Initial capacity of stack in bytes. */ - GenericDocument(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(allocator, stackCapacity), parseError_(0), errorOffset_(0) {} + GenericDocument(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(allocator, stackCapacity), parseErrorCode_(kParseErrorNone), errorOffset_(0) {} //! Parse JSON text from an input stream. /*! \tparam parseFlags Combination of ParseFlag. @@ -802,11 +802,11 @@ public: if (reader.template Parse(is, *this)) { RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object this->RawAssign(*stack_.template Pop(1)); // Add this-> to prevent issue 13. - parseError_ = 0; + parseErrorCode_ = kParseErrorNone; errorOffset_ = 0; } else { - parseError_ = reader.GetParseError(); + parseErrorCode_ = reader.GetParseErrorCode(); errorOffset_ = reader.GetErrorOffset(); ClearStack(); } @@ -851,10 +851,10 @@ public: } //! Whether a parse error was occured in the last parsing. - bool HasParseError() const { return parseError_ != 0; } + bool HasParseError() const { return parseErrorCode_ != kParseErrorNone; } //! Get the message of parsing error. - const char* GetParseError() const { return parseError_; } + ParseErrorCode GetParseError() const { return parseErrorCode_; } //! Get the offset in character of the parsing error. size_t GetErrorOffset() const { return errorOffset_; } @@ -914,7 +914,7 @@ private: static const size_t kDefaultStackCapacity = 1024; internal::Stack stack_; - const char* parseError_; + ParseErrorCode parseErrorCode_; size_t errorOffset_; }; diff --git a/include/rapidjson/error/en.h b/include/rapidjson/error/en.h new file mode 100644 index 0000000..bbdb024 --- /dev/null +++ b/include/rapidjson/error/en.h @@ -0,0 +1,41 @@ +#ifndef RAPIDJSON_ERROR_EN_H__ +#define RAPIDJSON_ERROR_EN_H__ + +#include "error.h" + +namespace rapidjson { + +inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErrorCode) { + switch (parseErrorCode) { + 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."); + + case kParseErrorObjectMissName: return RAPIDJSON_ERROR_STRING("Missing a name for object member."); + case kParseErrorObjectMissColon: return RAPIDJSON_ERROR_STRING("Missing a colon after a name of object member."); + case kParseErrorObjectMissCommaOrCurlyBracket: return RAPIDJSON_ERROR_STRING("Missing a comma or '}' after an object member."); + + case kParseErrorArrayMissCommaOrSquareBracket: return RAPIDJSON_ERROR_STRING("Must be a comma or ']' after an array element."); + + case kParseErrorStringUnicodeEscapeInvalidHex: return RAPIDJSON_ERROR_STRING("Incorrect hex digit after \\u escape in string."); + case kParseErrorStringUnicodeSurrogateInvalid: return RAPIDJSON_ERROR_STRING("The surrogate pair in string is invalid."); + case kParseErrorStringEscapeInvalid: return RAPIDJSON_ERROR_STRING("Invalid escape character in string."); + case kParseErrorStringMissQuotationMark: return RAPIDJSON_ERROR_STRING("Missing a closing quotation mark in string."); + case kParseErrorStringInvalidEncoding: return RAPIDJSON_ERROR_STRING("Invalid encoidng in string."); + + case kParesErrorNumberTooBig: return RAPIDJSON_ERROR_STRING("Number too big to store in double."); + case kParseErrorNumberMissFraction: return RAPIDJSON_ERROR_STRING("Miss fraction part in number."); + case kParseErrorNumberMissExponent: return RAPIDJSON_ERROR_STRING("Miss exponent in number."); + + default: + return RAPIDJSON_ERROR_STRING("Unknown error."); + } +} + +} // namespace rapidjson + +#endif // RAPIDJSON_ERROR_EN_H__ diff --git a/include/rapidjson/error/error.h b/include/rapidjson/error/error.h new file mode 100644 index 0000000..a24178a --- /dev/null +++ b/include/rapidjson/error/error.h @@ -0,0 +1,24 @@ +#ifndef RAPIDJSON_ERROR_ERROR_H__ +#define RAPIDJSON_ERROR_ERROR_H__ + +// For example, on Windows, user can define this macro as TCHAR +#ifndef RAPIDJSON_ERROR_CHARTYPE +#define RAPIDJSON_ERROR_CHARTYPE char +#endif + +// For example, on Windows, user can define this macro as _T(x) +#ifndef RAPIDJSON_ERROR_STRING +#define RAPIDJSON_ERROR_STRING(x) x +#endif + +namespace rapidjson { + +// User can dynamically change locale in runtime, e.g.: +// GetParseErrorFunc GetParseError = GetParseError_En; // or whatever +// const RAPIDJSON_ERROR_CHARTYPE* s = GetParseError(document.GetParseErrorCode()); + +typedef const RAPIDJSON_ERROR_CHARTYPE* (*GetParseErrorFunc)(ParseErrorCode); + +} // namespace rapidjson + +#endif // RAPIDJSON_ERROR_ERROR_H__ \ No newline at end of file diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index 4a184c0..1b83485 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -21,19 +21,19 @@ #endif #ifndef RAPIDJSON_PARSE_ERROR_NORETURN -#define RAPIDJSON_PARSE_ERROR_NORETURN(msg, offset) \ +#define RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset) \ RAPIDJSON_MULTILINEMACRO_BEGIN \ if (!HasParseError()) {\ - parseError_ = msg; \ + parseErrorCode_ = parseErrorCode; \ errorOffset_ = offset; \ }\ RAPIDJSON_MULTILINEMACRO_END #endif #ifndef RAPIDJSON_PARSE_ERROR -#define RAPIDJSON_PARSE_ERROR(msg, offset) \ +#define RAPIDJSON_PARSE_ERROR(parseErrorCode, offset) \ RAPIDJSON_MULTILINEMACRO_BEGIN \ - RAPIDJSON_PARSE_ERROR_NORETURN(msg, offset); \ + RAPIDJSON_PARSE_ERROR_NORETURN(parseErrorCode, offset); \ return; \ RAPIDJSON_MULTILINEMACRO_END #endif @@ -50,6 +50,33 @@ enum ParseFlag { kParseValidateEncodingFlag = 2 //!< Validate encoding of JSON strings. }; +//! Error code of parsing. +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. + + kParseErrorObjectMissName, //!< Missing a name for object member. + kParseErrorObjectMissColon, //!< Missing a colon after a name of object member. + kParseErrorObjectMissCommaOrCurlyBracket, //!< Missing a comma or '}' after an object member. + + kParseErrorArrayMissCommaOrSquareBracket, //!< Must be a comma or ']' after an array element. + + kParseErrorStringUnicodeEscapeInvalidHex, //!< Incorrect hex digit after \\u escape in string. + kParseErrorStringUnicodeSurrogateInvalid, //!< The surrogate pair in string is invalid. + kParseErrorStringEscapeInvalid, //!< Invalid escape character in string. + kParseErrorStringMissQuotationMark, //!< Missing a closing quotation mark in string. + kParseErrorStringInvalidEncoding, //!< Invalid encoidng in string. + + kParesErrorNumberTooBig, //!< Number too big to store in double. + kParseErrorNumberMissFraction, //!< Miss fraction part in number. + kParseErrorNumberMissExponent //!< Miss exponent in number. +}; + /////////////////////////////////////////////////////////////////////////////// // Handler @@ -218,7 +245,7 @@ public: /*! \param allocator Optional allocator for allocating stack memory. (Only use for non-destructive parsing) \param stackCapacity stack capacity in bytes for storing a single decoded string. (Only use for non-destructive parsing) */ - GenericReader(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(allocator, stackCapacity), parseError_(0), errorOffset_(0) {} + GenericReader(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(allocator, stackCapacity), parseErrorCode_(kParseErrorNone), errorOffset_(0) {} //! Parse JSON text. /*! \tparam parseFlags Combination of ParseFlag. @@ -230,31 +257,31 @@ public: */ template bool Parse(InputStream& is, Handler& handler) { - parseError_ = 0; + parseErrorCode_ = kParseErrorNone; errorOffset_ = 0; SkipWhitespace(is); if (is.Peek() == '\0') - RAPIDJSON_PARSE_ERROR_NORETURN("Text only contains white space(s)", is.Tell()); + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentEmpty, is.Tell()); else { switch (is.Peek()) { case '{': ParseObject(is, handler); break; case '[': ParseArray(is, handler); break; - default: RAPIDJSON_PARSE_ERROR_NORETURN("Expect either an object or array at root", is.Tell()); + default: RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotObjectOrArray, is.Tell()); } SkipWhitespace(is); if (is.Peek() != '\0') - RAPIDJSON_PARSE_ERROR_NORETURN("Nothing should follow the root object or array.", is.Tell()); + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorDocumentRootNotSingular, is.Tell()); } stack_.Clear(); return !HasParseError(); } - bool HasParseError() const { return parseError_ != 0; } - const char* GetParseError() const { return parseError_; } + bool HasParseError() const { return parseErrorCode_ != kParseErrorNone; } + ParseErrorCode GetParseErrorCode() const { return parseErrorCode_; } size_t GetErrorOffset() const { return errorOffset_; } private: @@ -274,7 +301,7 @@ private: for (SizeType memberCount = 0;;) { if (is.Peek() != '"') - RAPIDJSON_PARSE_ERROR("Name of an object member must be a string", is.Tell()); + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); ParseString(is, handler); if (HasParseError()) @@ -283,7 +310,7 @@ private: SkipWhitespace(is); if (is.Take() != ':') - RAPIDJSON_PARSE_ERROR("There must be a colon after the name of object member", is.Tell()); + RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissColon, is.Tell()); SkipWhitespace(is); @@ -298,7 +325,7 @@ private: switch(is.Take()) { case ',': SkipWhitespace(is); break; case '}': handler.EndObject(memberCount); return; - default: RAPIDJSON_PARSE_ERROR("Must be a comma or '}' after an object member", is.Tell()); + default: RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissCommaOrCurlyBracket, is.Tell()); } } } @@ -328,7 +355,7 @@ private: switch (is.Take()) { case ',': SkipWhitespace(is); break; case ']': handler.EndArray(elementCount); return; - default: RAPIDJSON_PARSE_ERROR("Must be a comma or ']' after an array element.", is.Tell()); + default: RAPIDJSON_PARSE_ERROR(kParseErrorArrayMissCommaOrSquareBracket, is.Tell()); } } } @@ -341,7 +368,7 @@ private: if (is.Take() == 'u' && is.Take() == 'l' && is.Take() == 'l') handler.Null(); else - RAPIDJSON_PARSE_ERROR("Invalid value", is.Tell() - 1); + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); } template @@ -352,7 +379,7 @@ private: if (is.Take() == 'r' && is.Take() == 'u' && is.Take() == 'e') handler.Bool(true); else - RAPIDJSON_PARSE_ERROR("Invalid value", is.Tell()); + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); } template @@ -363,7 +390,7 @@ private: if (is.Take() == 'a' && is.Take() == 'l' && is.Take() == 's' && is.Take() == 'e') handler.Bool(false); else - RAPIDJSON_PARSE_ERROR("Invalid value", is.Tell() - 1); + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); } // Helper function to parse four hexidecimal digits in \uXXXX in ParseString(). @@ -382,7 +409,7 @@ private: else if (c >= 'a' && c <= 'f') codepoint -= 'a' - 10; else { - RAPIDJSON_PARSE_ERROR_NORETURN("Incorrect hex digit after \\u escape", s.Tell() - 1); + RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStringUnicodeEscapeInvalidHex, s.Tell() - 1); return 0; } } @@ -455,16 +482,16 @@ private: if (codepoint >= 0xD800 && codepoint <= 0xDBFF) { // Handle UTF-16 surrogate pair if (is.Take() != '\\' || is.Take() != 'u') - RAPIDJSON_PARSE_ERROR("Missing the second \\u in surrogate pair", is.Tell() - 2); + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, is.Tell() - 2); unsigned codepoint2 = ParseHex4(is); if (codepoint2 < 0xDC00 || codepoint2 > 0xDFFF) - RAPIDJSON_PARSE_ERROR("The second \\u in surrogate pair is invalid", is.Tell() - 2); + RAPIDJSON_PARSE_ERROR(kParseErrorStringUnicodeSurrogateInvalid, is.Tell() - 2); codepoint = (((codepoint - 0xD800) << 10) | (codepoint2 - 0xDC00)) + 0x10000; } TEncoding::Encode(os, codepoint); } else - RAPIDJSON_PARSE_ERROR("Unknown escape character", is.Tell() - 1); + RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, is.Tell() - 1); } else if (c == '"') { // Closing double quote is.Take(); @@ -472,14 +499,14 @@ private: return; } else if (c == '\0') - RAPIDJSON_PARSE_ERROR("lacks ending quotation before the end of string", is.Tell() - 1); + RAPIDJSON_PARSE_ERROR(kParseErrorStringMissQuotationMark, is.Tell() - 1); else if ((unsigned)c < 0x20) // RFC 4627: unescaped = %x20-21 / %x23-5B / %x5D-10FFFF - RAPIDJSON_PARSE_ERROR("Incorrect unescaped character in string", is.Tell() - 1); + RAPIDJSON_PARSE_ERROR(kParseErrorStringEscapeInvalid, is.Tell() - 1); else { if (parseFlags & kParseValidateEncodingFlag ? !Transcoder::Validate(is, os) : !Transcoder::Transcode(is, os)) - RAPIDJSON_PARSE_ERROR("Invalid encoding", is.Tell()); + RAPIDJSON_PARSE_ERROR(kParseErrorStringInvalidEncoding, is.Tell()); } } } @@ -526,7 +553,7 @@ private: } } else - RAPIDJSON_PARSE_ERROR("Expect a value here.", is.Tell()); + RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell()); // Parse 64bit int uint64_t i64 = 0; @@ -559,7 +586,7 @@ private: d = (double)i64; while (s.Peek() >= '0' && s.Peek() <= '9') { if (d >= 1E307) - RAPIDJSON_PARSE_ERROR("Number too big to store in double", is.Tell()); + RAPIDJSON_PARSE_ERROR(kParesErrorNumberTooBig, is.Tell()); d = d * 10 + (s.Take() - '0'); } } @@ -578,7 +605,7 @@ private: --expFrac; } else - RAPIDJSON_PARSE_ERROR("At least one digit in fraction part", is.Tell()); + RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissFraction, is.Tell()); while (s.Peek() >= '0' && s.Peek() <= '9') { if (expFrac > -16) { @@ -611,11 +638,11 @@ private: while (s.Peek() >= '0' && s.Peek() <= '9') { exp = exp * 10 + (s.Take() - '0'); if (exp > 308) - RAPIDJSON_PARSE_ERROR("Number too big to store in double", is.Tell()); + RAPIDJSON_PARSE_ERROR(kParesErrorNumberTooBig, is.Tell()); } } else - RAPIDJSON_PARSE_ERROR("At least one digit in exponent", s.Tell()); + RAPIDJSON_PARSE_ERROR(kParseErrorNumberMissExponent, s.Tell()); if (expMinus) exp = -exp; @@ -660,7 +687,7 @@ private: static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string. internal::Stack stack_; //!< A stack for storing decoded string temporarily during non-destructive parsing. - const char* parseError_; + ParseErrorCode parseErrorCode_; size_t errorOffset_; }; // class GenericReader