6 #if !defined(JSON_IS_AMALGAMATION)
11 #endif // if !defined(JSON_IS_AMALGAMATION)
18 #if defined(_MSC_VER) && _MSC_VER < 1500 // VC++ 8.0 and below
19 #define snprintf _snprintf
22 #if defined(_MSC_VER) && _MSC_VER >= 1400 // VC++ 8.0
24 #pragma warning(disable : 4996)
33 : allowComments_(true), strictRoot_(false),
34 allowDroppedNullPlaceholders_(false), allowNumericKeys_(false) {}
55 return c == c1 || c == c2 || c == c3 || c == c4;
64 return c == c1 || c == c2 || c == c3 || c == c4 || c == c5;
68 for (; begin < end; ++begin)
69 if (*begin ==
'\n' || *begin ==
'\r')
78 : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(),
79 lastValue_(), commentsBefore_(), features_(
Features::all()),
83 : errors_(), document_(), begin_(), end_(), current_(), lastValueEnd_(),
84 lastValue_(), commentsBefore_(), features_(features), collectComments_() {
90 const char* begin = document_.c_str();
91 const char* end = begin + document_.length();
92 return parse(begin, end, root, collectComments);
104 std::getline(sin, doc, (
char)EOF);
105 return parse(doc, root, collectComments);
111 bool collectComments) {
113 collectComments =
false;
118 collectComments_ = collectComments;
122 commentsBefore_ =
"";
124 while (!nodes_.empty())
128 bool successful = readValue();
130 skipCommentTokens(token);
131 if (collectComments_ && !commentsBefore_.empty())
137 token.type_ = tokenError;
138 token.start_ = beginDoc;
141 "A valid JSON document must be either an array or an object value.",
149 bool Reader::readValue() {
151 skipCommentTokens(token);
152 bool successful =
true;
154 if (collectComments_ && !commentsBefore_.empty()) {
156 size_t lastNonNewline = commentsBefore_.find_last_not_of(
"\r\n");
157 if (lastNonNewline != std::string::npos) {
158 commentsBefore_.erase(lastNonNewline + 1);
160 commentsBefore_.clear();
164 commentsBefore_ =
"";
167 switch (token.type_) {
168 case tokenObjectBegin:
169 successful = readObject(token);
172 case tokenArrayBegin:
173 successful = readArray(token);
177 successful = decodeNumber(token);
180 successful = decodeString(token);
183 currentValue() =
true;
188 currentValue() =
false;
193 currentValue() = Value();
197 case tokenArraySeparator:
202 currentValue() = Value();
211 return addError(
"Syntax error: value, object or array expected.", token);
214 if (collectComments_) {
215 lastValueEnd_ = current_;
216 lastValue_ = ¤tValue();
222 void Reader::skipCommentTokens(Token& token) {
226 }
while (token.type_ == tokenComment);
232 bool Reader::expectToken(TokenType type, Token& token,
const char* message) {
234 if (token.type_ != type)
235 return addError(message, token);
239 bool Reader::readToken(Token& token) {
241 token.start_ = current_;
242 Char c = getNextChar();
246 token.type_ = tokenObjectBegin;
249 token.type_ = tokenObjectEnd;
252 token.type_ = tokenArrayBegin;
255 token.type_ = tokenArrayEnd;
258 token.type_ = tokenString;
262 token.type_ = tokenComment;
276 token.type_ = tokenNumber;
280 token.type_ = tokenTrue;
281 ok = match(
"rue", 3);
284 token.type_ = tokenFalse;
285 ok = match(
"alse", 4);
288 token.type_ = tokenNull;
289 ok = match(
"ull", 3);
292 token.type_ = tokenArraySeparator;
295 token.type_ = tokenMemberSeparator;
298 token.type_ = tokenEndOfStream;
305 token.type_ = tokenError;
306 token.end_ = current_;
310 void Reader::skipSpaces() {
311 while (current_ != end_) {
313 if (c ==
' ' || c ==
'\t' || c ==
'\r' || c ==
'\n')
320 bool Reader::match(Location pattern,
int patternLength) {
321 if (end_ - current_ < patternLength)
323 int index = patternLength;
325 if (current_[index] != pattern[index])
327 current_ += patternLength;
331 bool Reader::readComment() {
332 Location commentBegin = current_ - 1;
333 Char c = getNextChar();
334 bool successful =
false;
336 successful = readCStyleComment();
338 successful = readCppStyleComment();
342 if (collectComments_) {
349 addComment(commentBegin, current_, placement);
355 Reader::addComment(Location begin, Location end,
CommentPlacement placement) {
356 assert(collectComments_);
358 assert(lastValue_ != 0);
359 lastValue_->
setComment(std::string(begin, end), placement);
361 commentsBefore_ += std::string(begin, end);
365 bool Reader::readCStyleComment() {
366 while (current_ != end_) {
367 Char c = getNextChar();
368 if (c ==
'*' && *current_ ==
'/')
371 return getNextChar() ==
'/';
374 bool Reader::readCppStyleComment() {
375 while (current_ != end_) {
376 Char c = getNextChar();
377 if (c ==
'\r' || c ==
'\n')
383 void Reader::readNumber() {
384 while (current_ != end_) {
385 if (!(*current_ >=
'0' && *current_ <=
'9') &&
386 !
in(*current_,
'.',
'e',
'E',
'+',
'-'))
392 bool Reader::readString() {
394 while (current_ != end_) {
404 bool Reader::readObject(Token& tokenStart) {
409 while (readToken(tokenName)) {
410 bool initialTokenOk =
true;
411 while (tokenName.type_ == tokenComment && initialTokenOk)
412 initialTokenOk = readToken(tokenName);
415 if (tokenName.type_ == tokenObjectEnd && name.empty())
418 if (tokenName.type_ == tokenString) {
419 if (!decodeString(tokenName, name))
420 return recoverFromError(tokenObjectEnd);
423 if (!decodeNumber(tokenName, numberName))
424 return recoverFromError(tokenObjectEnd);
425 name = numberName.asString();
431 if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
432 return addErrorAndRecover(
433 "Missing ':' after object member name", colon, tokenObjectEnd);
435 Value& value = currentValue()[name];
437 bool ok = readValue();
440 return recoverFromError(tokenObjectEnd);
443 if (!readToken(comma) ||
444 (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator &&
445 comma.type_ != tokenComment)) {
446 return addErrorAndRecover(
447 "Missing ',' or '}' in object declaration", comma, tokenObjectEnd);
449 bool finalizeTokenOk =
true;
450 while (comma.type_ == tokenComment && finalizeTokenOk)
451 finalizeTokenOk = readToken(comma);
452 if (comma.type_ == tokenObjectEnd)
455 return addErrorAndRecover(
456 "Missing '}' or object member name", tokenName, tokenObjectEnd);
459 bool Reader::readArray(Token& tokenStart) {
463 if (*current_ ==
']')
471 Value& value = currentValue()[index++];
473 bool ok = readValue();
476 return recoverFromError(tokenArrayEnd);
480 ok = readToken(token);
481 while (token.type_ == tokenComment && ok) {
482 ok = readToken(token);
485 (token.type_ != tokenArraySeparator && token.type_ != tokenArrayEnd);
486 if (!ok || badTokenType) {
487 return addErrorAndRecover(
488 "Missing ',' or ']' in array declaration", token, tokenArrayEnd);
490 if (token.type_ == tokenArrayEnd)
496 bool Reader::decodeNumber(Token& token) {
498 if (!decodeNumber(token, decoded))
500 currentValue() = decoded;
506 bool Reader::decodeNumber(Token& token, Value& decoded) {
507 bool isDouble =
false;
508 for (
Location inspect = token.start_; inspect != token.end_; ++inspect) {
509 isDouble = isDouble ||
in(*inspect,
'.',
'e',
'E',
'+') ||
510 (*inspect ==
'-' && inspect != token.start_);
513 return decodeDouble(token, decoded);
518 bool isNegative = *current ==
'-';
523 : Value::maxLargestUInt;
524 Value::LargestUInt threshold = maxIntegerValue / 10;
525 Value::LargestUInt value = 0;
526 while (current < token.end_) {
528 if (c < '0' || c >
'9')
529 return addError(
"'" + std::string(token.start_, token.end_) +
530 "' is not a number.",
533 if (value >= threshold) {
538 if (value > threshold || current != token.end_ ||
539 digit > maxIntegerValue % 10) {
540 return decodeDouble(token, decoded);
543 value = value * 10 + digit;
554 bool Reader::decodeDouble(Token& token) {
556 if (!decodeDouble(token, decoded))
558 currentValue() = decoded;
564 bool Reader::decodeDouble(Token& token, Value& decoded) {
566 const int bufferSize = 32;
568 int length = int(token.end_ - token.start_);
572 return addError(
"Unable to parse token length", token);
580 char format[] =
"%lf";
582 if (length <= bufferSize) {
583 Char buffer[bufferSize + 1];
584 memcpy(buffer, token.start_, length);
586 count = sscanf(buffer, format, &value);
588 std::string buffer(token.start_, token.end_);
589 count = sscanf(buffer.c_str(), format, &value);
593 return addError(
"'" + std::string(token.start_, token.end_) +
594 "' is not a number.",
600 bool Reader::decodeString(Token& token) {
602 if (!decodeString(token, decoded))
604 currentValue() = decoded;
610 bool Reader::decodeString(Token& token, std::string& decoded) {
611 decoded.reserve(token.end_ - token.start_ - 2);
612 Location current = token.start_ + 1;
614 while (current != end) {
618 else if (c ==
'\\') {
620 return addError(
"Empty escape sequence in string", token, current);
621 Char escape = *current++;
648 unsigned int unicode;
649 if (!decodeUnicodeCodePoint(token, current, end, unicode))
654 return addError(
"Bad escape sequence in string", token, current);
663 bool Reader::decodeUnicodeCodePoint(Token& token,
666 unsigned int& unicode) {
668 if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
670 if (unicode >= 0xD800 && unicode <= 0xDBFF) {
672 if (end - current < 6)
674 "additional six characters expected to parse unicode surrogate pair.",
677 unsigned int surrogatePair;
678 if (*(current++) ==
'\\' && *(current++) ==
'u') {
679 if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
680 unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
684 return addError(
"expecting another \\u token to begin the second half of "
685 "a unicode surrogate pair",
692 bool Reader::decodeUnicodeEscapeSequence(Token& token,
695 unsigned int& unicode) {
696 if (end - current < 4)
698 "Bad unicode escape sequence in string: four digits expected.",
702 for (
int index = 0; index < 4; ++index) {
705 if (c >=
'0' && c <=
'9')
707 else if (c >=
'a' && c <=
'f')
708 unicode += c -
'a' + 10;
709 else if (c >=
'A' && c <=
'F')
710 unicode += c -
'A' + 10;
713 "Bad unicode escape sequence in string: hexadecimal digit expected.",
721 Reader::addError(
const std::string& message, Token& token, Location extra) {
724 info.message_ = message;
726 errors_.push_back(info);
730 bool Reader::recoverFromError(TokenType skipUntilToken) {
731 int errorCount = int(errors_.size());
734 if (!readToken(skip))
735 errors_.resize(errorCount);
736 if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
739 errors_.resize(errorCount);
743 bool Reader::addErrorAndRecover(
const std::string& message,
745 TokenType skipUntilToken) {
746 addError(message, token);
747 return recoverFromError(skipUntilToken);
750 Value& Reader::currentValue() {
return *(nodes_.top()); }
753 if (current_ == end_)
758 void Reader::getLocationLineAndColumn(Location location,
764 while (current < location && current != end_) {
767 if (*current ==
'\n')
769 lastLineStart = current;
771 }
else if (c ==
'\n') {
772 lastLineStart = current;
777 column = int(location - lastLineStart) + 1;
781 std::string Reader::getLocationLineAndColumn(Location location)
const {
783 getLocationLineAndColumn(location, line, column);
784 char buffer[18 + 16 + 16 + 1];
785 #if defined(_MSC_VER) && defined(__STDC_SECURE_LIB__)
787 _snprintf(buffer,
sizeof(buffer),
"Line %d, Column %d", line, column);
789 sprintf_s(buffer,
sizeof(buffer),
"Line %d, Column %d", line, column);
792 snprintf(buffer,
sizeof(buffer),
"Line %d, Column %d", line, column);
803 std::string formattedMessage;
804 for (Errors::const_iterator itError = errors_.begin();
805 itError != errors_.end();
807 const ErrorInfo& error = *itError;
809 "* " + getLocationLineAndColumn(error.token_.start_) +
"\n";
810 formattedMessage +=
" " + error.message_ +
"\n";
813 "See " + getLocationLineAndColumn(error.extra_) +
" for detail.\n";
815 return formattedMessage;
819 std::vector<Reader::StructuredError> allErrors;
820 for (Errors::const_iterator itError = errors_.begin();
821 itError != errors_.end();
823 const ErrorInfo& error = *itError;
827 structured.
message = error.message_;
828 allErrors.push_back(structured);
838 token.type_ = tokenError;
843 info.message_ = message;
845 errors_.push_back(info);
855 token.type_ = tokenError;
860 info.message_ = message;
862 errors_.push_back(info);
867 return !errors_.size();
872 bool ok = reader.
parse(sin, root,
true);
875 "Error from reader: %s",
static std::string codePointToUTF8(unsigned int cp)
Converts a unicode code-point to UTF-8.
array value (ordered list)
object value (collection of name/value pairs).
std::istream & operator>>(std::istream &, Value &)
Read from 'sin' into 'root'.
std::string getFormatedErrorMessages() const
Returns a user friendly string that list errors in the parsed document.
void setOffsetStart(size_t start)
static const Int maxInt
Maximum signed int value that can be stored in a Json::Value.
Json::LargestUInt LargestUInt
Features()
Initialize the configuration like JsonConfig::allFeatures;.
An error tagged with where in the JSON text it was encountered.
std::vector< StructuredError > getStructuredErrors() const
Returns a vector of structured erros encounted while parsing.
void setComment(const char *comment, CommentPlacement placement)
Comments must be //... or /* ... */.
static const LargestInt minLargestInt
Minimum signed integer value that can be stored in a Json::Value.
bool allowComments_
true if comments are allowed. Default: true.
bool allowNumericKeys_
true if numeric object key are allowed. Default: false.
size_t getOffsetLimit() const
bool good() const
Return whether there are any errors.
bool parse(const std::string &document, Value &root, bool collectComments=true)
Read a Value from a JSON document.
static bool in(Reader::Char c, Reader::Char c1, Reader::Char c2, Reader::Char c3, Reader::Char c4)
bool allowDroppedNullPlaceholders_
true if dropped null placeholders are allowed. Default: false.
#define JSON_FAIL_MESSAGE(message)
Json::LargestInt LargestInt
void setOffsetLimit(size_t limit)
static Features all()
A configuration that allows all features and assumes all strings are UTF-8.
a comment on the line after a value (only make sense for
Unserialize a JSON document into a Value.
bool pushError(const Value &value, const std::string &message)
Add a semantic error message.
static Features strictMode()
A configuration that is strictly compatible with the JSON specification.
bool strictRoot_
true if root must be either an array or an object value.
size_t getOffsetStart() const
static bool containsNewLine(Reader::Location begin, Reader::Location end)
Configuration passed to reader and writer.
a comment placed on the line before a value
Reader()
Constructs a Reader allowing all features for parsing.
std::string getFormattedErrorMessages() const
Returns a user friendly string that list errors in the parsed document.
a comment just after a value on the same line