7#if !defined(JSON_IS_AMALGAMATION)
25#if __cplusplus >= 201103L
28#define sscanf std::sscanf
34#if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES)
35#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
41#pragma warning(disable : 4996)
46#if !defined(JSONCPP_DEPRECATED_STACK_LIMIT)
47#define JSONCPP_DEPRECATED_STACK_LIMIT 1000
55#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
81 return std::any_of(begin, end, [](
char b) {
return b ==
'\n' || b ==
'\r'; });
92 bool collectComments) {
93 document_.assign(document.begin(), document.end());
94 const char* begin = document_.c_str();
95 const char* end = begin + document_.length();
96 return parse(begin, end, root, collectComments);
107 String doc(std::istreambuf_iterator<char>(is), {});
108 return parse(doc.data(), doc.data() + doc.size(), root, collectComments);
112 bool collectComments) {
114 collectComments =
false;
119 collectComments_ = collectComments;
121 lastValueEnd_ =
nullptr;
122 lastValue_ =
nullptr;
123 commentsBefore_.clear();
125 while (!nodes_.empty())
129 bool successful = readValue();
131 skipCommentTokens(token);
132 if (collectComments_ && !commentsBefore_.empty())
138 token.type_ = tokenError;
139 token.start_ = beginDoc;
142 "A valid JSON document must be either an array or an object value.",
150bool Reader::readValue() {
156 throwRuntimeError(
"Exceeded stackLimit in readValue().");
159 skipCommentTokens(token);
160 bool successful =
true;
162 if (collectComments_ && !commentsBefore_.empty()) {
164 commentsBefore_.clear();
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);
200 case tokenArraySeparator:
216 return addError(
"Syntax error: value, object or array expected.", token);
219 if (collectComments_) {
220 lastValueEnd_ = current_;
221 lastValue_ = ¤tValue();
227void Reader::skipCommentTokens(Token& token) {
231 }
while (token.type_ == tokenComment);
237bool Reader::readToken(Token& token) {
239 token.start_ = current_;
240 Char c = getNextChar();
244 token.type_ = tokenObjectBegin;
247 token.type_ = tokenObjectEnd;
250 token.type_ = tokenArrayBegin;
253 token.type_ = tokenArrayEnd;
256 token.type_ = tokenString;
260 token.type_ = tokenComment;
274 token.type_ = tokenNumber;
278 token.type_ = tokenTrue;
279 ok = match(
"rue", 3);
282 token.type_ = tokenFalse;
283 ok = match(
"alse", 4);
286 token.type_ = tokenNull;
287 ok = match(
"ull", 3);
290 token.type_ = tokenArraySeparator;
293 token.type_ = tokenMemberSeparator;
296 token.type_ = tokenEndOfStream;
303 token.type_ = tokenError;
304 token.end_ = current_;
308void Reader::skipSpaces() {
309 while (current_ != end_) {
311 if (c ==
' ' || c ==
'\t' || c ==
'\r' || c ==
'\n')
318bool Reader::match(
const Char* pattern,
int patternLength) {
319 if (end_ - current_ < patternLength)
321 int index = patternLength;
323 if (current_[index] != pattern[index])
325 current_ += patternLength;
329bool Reader::readComment() {
330 Location commentBegin = current_ - 1;
331 Char c = getNextChar();
332 bool successful =
false;
334 successful = readCStyleComment();
336 successful = readCppStyleComment();
340 if (collectComments_) {
342 if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
343 if (c !=
'*' || !containsNewLine(commentBegin, current_))
347 addComment(commentBegin, current_, placement);
354 normalized.reserve(
static_cast<size_t>(end - begin));
356 while (current != end) {
359 if (current != end && *current ==
'\n')
371void Reader::addComment(Location begin, Location end,
373 assert(collectComments_);
374 const String& normalized = normalizeEOL(begin, end);
376 assert(lastValue_ !=
nullptr);
377 lastValue_->
setComment(normalized, placement);
379 commentsBefore_ += normalized;
383bool Reader::readCStyleComment() {
384 while ((current_ + 1) < end_) {
385 Char c = getNextChar();
386 if (c ==
'*' && *current_ ==
'/')
389 return getNextChar() ==
'/';
392bool Reader::readCppStyleComment() {
393 while (current_ != end_) {
394 Char c = getNextChar();
399 if (current_ != end_ && *current_ ==
'\n')
408void Reader::readNumber() {
412 while (c >=
'0' && c <=
'9')
413 c = (current_ = p) < end_ ? *p++ :
'\0';
416 c = (current_ = p) < end_ ? *p++ :
'\0';
417 while (c >=
'0' && c <=
'9')
418 c = (current_ = p) < end_ ? *p++ :
'\0';
421 if (c ==
'e' || c ==
'E') {
422 c = (current_ = p) < end_ ? *p++ :
'\0';
423 if (c ==
'+' || c ==
'-')
424 c = (current_ = p) < end_ ? *p++ :
'\0';
425 while (c >=
'0' && c <=
'9')
426 c = (current_ = p) < end_ ? *p++ :
'\0';
430bool Reader::readString() {
432 while (current_ != end_) {
442bool Reader::readObject(Token& token) {
448 while (readToken(tokenName)) {
449 bool initialTokenOk =
true;
450 while (tokenName.type_ == tokenComment && initialTokenOk)
451 initialTokenOk = readToken(tokenName);
454 if (tokenName.type_ == tokenObjectEnd && name.empty())
457 if (tokenName.type_ == tokenString) {
458 if (!decodeString(tokenName, name))
459 return recoverFromError(tokenObjectEnd);
462 if (!decodeNumber(tokenName, numberName))
463 return recoverFromError(tokenObjectEnd);
464 name = numberName.asString();
470 if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
471 return addErrorAndRecover(
"Missing ':' after object member name", colon,
474 Value& value = currentValue()[name];
476 bool ok = readValue();
479 return recoverFromError(tokenObjectEnd);
482 if (!readToken(comma) ||
483 (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator &&
484 comma.type_ != tokenComment)) {
485 return addErrorAndRecover(
"Missing ',' or '}' in object declaration",
486 comma, tokenObjectEnd);
488 bool finalizeTokenOk =
true;
489 while (comma.type_ == tokenComment && finalizeTokenOk)
490 finalizeTokenOk = readToken(comma);
491 if (comma.type_ == tokenObjectEnd)
494 return addErrorAndRecover(
"Missing '}' or object member name", tokenName,
498bool Reader::readArray(Token& token) {
503 if (current_ != end_ && *current_ ==
']')
511 Value& value = currentValue()[index++];
513 bool ok = readValue();
516 return recoverFromError(tokenArrayEnd);
520 ok = readToken(currentToken);
521 while (currentToken.type_ == tokenComment && ok) {
522 ok = readToken(currentToken);
524 bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
525 currentToken.type_ != tokenArrayEnd);
526 if (!ok || badTokenType) {
527 return addErrorAndRecover(
"Missing ',' or ']' in array declaration",
528 currentToken, tokenArrayEnd);
530 if (currentToken.type_ == tokenArrayEnd)
536bool Reader::decodeNumber(Token& token) {
538 if (!decodeNumber(token, decoded))
546bool Reader::decodeNumber(Token& token, Value& decoded) {
551 bool isNegative = *current ==
'-';
561 while (current < token.end_) {
563 if (c < '0' || c >
'9')
564 return decodeDouble(token, decoded);
566 if (value >= threshold) {
571 if (value > threshold || current != token.end_ ||
572 digit > maxIntegerValue % 10) {
573 return decodeDouble(token, decoded);
576 value = value * 10 + digit;
578 if (isNegative && value == maxIntegerValue)
589bool Reader::decodeDouble(Token& token) {
591 if (!decodeDouble(token, decoded))
599bool Reader::decodeDouble(Token& token, Value& decoded) {
601 String buffer(token.start_, token.end_);
605 "'" +
String(token.start_, token.end_) +
"' is not a number.", token);
610bool Reader::decodeString(Token& token) {
612 if (!decodeString(token, decoded_string))
614 Value decoded(decoded_string);
621bool Reader::decodeString(Token& token,
String& decoded) {
622 decoded.reserve(
static_cast<size_t>(token.end_ - token.start_ - 2));
623 Location current = token.start_ + 1;
625 while (current != end) {
631 return addError(
"Empty escape sequence in string", token, current);
632 Char escape = *current++;
659 unsigned int unicode;
660 if (!decodeUnicodeCodePoint(token, current, end, unicode))
665 return addError(
"Bad escape sequence in string", token, current);
674bool Reader::decodeUnicodeCodePoint(Token& token, Location& current,
675 Location end,
unsigned int& unicode) {
677 if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
679 if (unicode >= 0xD800 && unicode <= 0xDBFF) {
681 if (end - current < 6)
683 "additional six characters expected to parse unicode surrogate pair.",
685 if (*(current++) ==
'\\' && *(current++) ==
'u') {
686 unsigned int surrogatePair;
687 if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
688 unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
692 return addError(
"expecting another \\u token to begin the second half of "
693 "a unicode surrogate pair",
699bool Reader::decodeUnicodeEscapeSequence(Token& token, Location& current,
701 unsigned int& ret_unicode) {
702 if (end - current < 4)
704 "Bad unicode escape sequence in string: four digits expected.", token,
707 for (
int index = 0; index < 4; ++index) {
710 if (c >=
'0' && c <=
'9')
712 else if (c >=
'a' && c <=
'f')
713 unicode += c -
'a' + 10;
714 else if (c >=
'A' && c <=
'F')
715 unicode += c -
'A' + 10;
718 "Bad unicode escape sequence in string: hexadecimal digit expected.",
721 ret_unicode =
static_cast<unsigned int>(unicode);
725bool Reader::addError(
const String& message, Token& token, Location extra) {
728 info.message_ = message;
730 errors_.push_back(info);
734bool Reader::recoverFromError(TokenType skipUntilToken) {
735 size_t const errorCount = errors_.size();
738 if (!readToken(skip))
739 errors_.resize(errorCount);
740 if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
743 errors_.resize(errorCount);
747bool Reader::addErrorAndRecover(
const String& message, Token& token,
748 TokenType skipUntilToken) {
749 addError(message, token);
750 return recoverFromError(skipUntilToken);
753Value& Reader::currentValue() {
return *(nodes_.top()); }
756 if (current_ == end_)
761void Reader::getLocationLineAndColumn(Location location,
int& line,
766 while (current < location && current != end_) {
769 if (*current ==
'\n')
771 lastLineStart = current;
773 }
else if (c ==
'\n') {
774 lastLineStart = current;
779 column = int(location - lastLineStart) + 1;
783String Reader::getLocationLineAndColumn(Location location)
const {
785 getLocationLineAndColumn(location, line, column);
786 char buffer[18 + 16 + 16 + 1];
787 jsoncpp_snprintf(buffer,
sizeof(buffer),
"Line %d, Column %d", line, column);
792String Reader::getFormatedErrorMessages()
const {
798 for (
const auto& error : errors_) {
800 "* " + getLocationLineAndColumn(error.token_.start_) +
"\n";
801 formattedMessage +=
" " + error.message_ +
"\n";
804 "See " + getLocationLineAndColumn(error.extra_) +
" for detail.\n";
806 return formattedMessage;
810 std::vector<Reader::StructuredError> allErrors;
811 for (
const auto& error : errors_) {
815 structured.
message = error.message_;
816 allErrors.push_back(structured);
822 ptrdiff_t
const length = end_ - begin_;
826 token.type_ = tokenError;
831 info.message_ = message;
832 info.extra_ =
nullptr;
833 errors_.push_back(info);
838 const Value& extra) {
839 ptrdiff_t
const length = end_ - begin_;
844 token.type_ = tokenError;
849 info.message_ = message;
851 errors_.push_back(info);
861 static OurFeatures all();
863 bool allowTrailingCommas_;
865 bool allowDroppedNullPlaceholders_;
866 bool allowNumericKeys_;
867 bool allowSingleQuotes_;
870 bool allowSpecialFloats_;
875OurFeatures OurFeatures::all() {
return {}; }
885 using Location =
const Char*;
886 struct StructuredError {
887 ptrdiff_t offset_start;
888 ptrdiff_t offset_limit;
892 explicit OurReader(OurFeatures
const& features);
893 bool parse(
const char* beginDoc,
const char* endDoc, Value& root,
894 bool collectComments =
true);
895 String getFormattedErrorMessages()
const;
896 std::vector<StructuredError> getStructuredErrors()
const;
899 OurReader(OurReader
const&);
900 void operator=(OurReader
const&);
903 tokenEndOfStream = 0,
917 tokenMemberSeparator,
936 using Errors = std::deque<ErrorInfo>;
938 bool readToken(Token& token);
940 void skipBom(
bool skipBom);
941 bool match(
const Char* pattern,
int patternLength);
943 bool readCStyleComment(
bool* containsNewLineResult);
944 bool readCppStyleComment();
946 bool readStringSingleQuote();
947 bool readNumber(
bool checkInf);
949 bool readObject(Token& token);
950 bool readArray(Token& token);
951 bool decodeNumber(Token& token);
952 bool decodeNumber(Token& token, Value& decoded);
953 bool decodeString(Token& token);
954 bool decodeString(Token& token,
String& decoded);
955 bool decodeDouble(Token& token);
956 bool decodeDouble(Token& token, Value& decoded);
957 bool decodeUnicodeCodePoint(Token& token, Location& current, Location end,
958 unsigned int& unicode);
959 bool decodeUnicodeEscapeSequence(Token& token, Location& current,
960 Location end,
unsigned int& unicode);
961 bool addError(
const String& message, Token& token, Location extra =
nullptr);
962 bool recoverFromError(TokenType skipUntilToken);
963 bool addErrorAndRecover(
const String& message, Token& token,
964 TokenType skipUntilToken);
965 void skipUntilSpace();
966 Value& currentValue();
968 void getLocationLineAndColumn(Location location,
int& line,
970 String getLocationLineAndColumn(Location location)
const;
972 void skipCommentTokens(Token& token);
974 static String normalizeEOL(Location begin, Location end);
975 static bool containsNewLine(Location begin, Location end);
977 using Nodes = std::stack<Value*>;
982 Location begin_ =
nullptr;
983 Location end_ =
nullptr;
984 Location current_ =
nullptr;
985 Location lastValueEnd_ =
nullptr;
986 Value* lastValue_ =
nullptr;
987 bool lastValueHasAComment_ =
false;
990 OurFeatures
const features_;
991 bool collectComments_ =
false;
996bool OurReader::containsNewLine(OurReader::Location begin,
997 OurReader::Location end) {
998 return std::any_of(begin, end, [](
char b) {
return b ==
'\n' || b ==
'\r'; });
1001OurReader::OurReader(OurFeatures
const& features) : features_(features) {}
1003bool OurReader::parse(
const char* beginDoc,
const char* endDoc, Value& root,
1004 bool collectComments) {
1005 if (!features_.allowComments_) {
1006 collectComments =
false;
1011 collectComments_ = collectComments;
1013 lastValueEnd_ =
nullptr;
1014 lastValue_ =
nullptr;
1015 commentsBefore_.clear();
1017 while (!nodes_.empty())
1022 skipBom(features_.skipBom_);
1023 bool successful = readValue();
1026 skipCommentTokens(token);
1027 if (features_.failIfExtra_ && (token.type_ != tokenEndOfStream)) {
1028 addError(
"Extra non-whitespace after JSON value.", token);
1031 if (collectComments_ && !commentsBefore_.empty())
1033 if (features_.strictRoot_) {
1034 if (!root.isArray() && !root.isObject()) {
1037 token.type_ = tokenError;
1038 token.start_ = beginDoc;
1039 token.end_ = endDoc;
1041 "A valid JSON document must be either an array or an object value.",
1049bool OurReader::readValue() {
1051 if (nodes_.size() > features_.stackLimit_)
1052 throwRuntimeError(
"Exceeded stackLimit in readValue().");
1054 skipCommentTokens(token);
1055 bool successful =
true;
1057 if (collectComments_ && !commentsBefore_.empty()) {
1059 commentsBefore_.clear();
1062 switch (token.type_) {
1063 case tokenObjectBegin:
1064 successful = readObject(token);
1065 currentValue().setOffsetLimit(current_ - begin_);
1067 case tokenArrayBegin:
1068 successful = readArray(token);
1069 currentValue().setOffsetLimit(current_ - begin_);
1072 successful = decodeNumber(token);
1075 successful = decodeString(token);
1079 currentValue().swapPayload(v);
1080 currentValue().setOffsetStart(token.start_ - begin_);
1081 currentValue().setOffsetLimit(token.end_ - begin_);
1085 currentValue().swapPayload(v);
1086 currentValue().setOffsetStart(token.start_ - begin_);
1087 currentValue().setOffsetLimit(token.end_ - begin_);
1091 currentValue().swapPayload(v);
1092 currentValue().setOffsetStart(token.start_ - begin_);
1093 currentValue().setOffsetLimit(token.end_ - begin_);
1096 Value v(std::numeric_limits<double>::quiet_NaN());
1097 currentValue().swapPayload(v);
1098 currentValue().setOffsetStart(token.start_ - begin_);
1099 currentValue().setOffsetLimit(token.end_ - begin_);
1102 Value v(std::numeric_limits<double>::infinity());
1103 currentValue().swapPayload(v);
1104 currentValue().setOffsetStart(token.start_ - begin_);
1105 currentValue().setOffsetLimit(token.end_ - begin_);
1108 Value v(-std::numeric_limits<double>::infinity());
1109 currentValue().swapPayload(v);
1110 currentValue().setOffsetStart(token.start_ - begin_);
1111 currentValue().setOffsetLimit(token.end_ - begin_);
1113 case tokenArraySeparator:
1114 case tokenObjectEnd:
1116 if (features_.allowDroppedNullPlaceholders_) {
1121 currentValue().swapPayload(v);
1122 currentValue().setOffsetStart(current_ - begin_ - 1);
1123 currentValue().setOffsetLimit(current_ - begin_);
1127 currentValue().setOffsetStart(token.start_ - begin_);
1128 currentValue().setOffsetLimit(token.end_ - begin_);
1129 return addError(
"Syntax error: value, object or array expected.", token);
1132 if (collectComments_) {
1133 lastValueEnd_ = current_;
1134 lastValueHasAComment_ =
false;
1135 lastValue_ = ¤tValue();
1141void OurReader::skipCommentTokens(Token& token) {
1142 if (features_.allowComments_) {
1145 }
while (token.type_ == tokenComment);
1151bool OurReader::readToken(Token& token) {
1153 token.start_ = current_;
1154 Char c = getNextChar();
1158 token.type_ = tokenObjectBegin;
1161 token.type_ = tokenObjectEnd;
1164 token.type_ = tokenArrayBegin;
1167 token.type_ = tokenArrayEnd;
1170 token.type_ = tokenString;
1174 if (features_.allowSingleQuotes_) {
1175 token.type_ = tokenString;
1176 ok = readStringSingleQuote();
1183 token.type_ = tokenComment;
1196 token.type_ = tokenNumber;
1200 if (readNumber(
true)) {
1201 token.type_ = tokenNumber;
1203 token.type_ = tokenNegInf;
1204 ok = features_.allowSpecialFloats_ && match(
"nfinity", 7);
1208 if (readNumber(
true)) {
1209 token.type_ = tokenNumber;
1211 token.type_ = tokenPosInf;
1212 ok = features_.allowSpecialFloats_ && match(
"nfinity", 7);
1216 token.type_ = tokenTrue;
1217 ok = match(
"rue", 3);
1220 token.type_ = tokenFalse;
1221 ok = match(
"alse", 4);
1224 token.type_ = tokenNull;
1225 ok = match(
"ull", 3);
1228 if (features_.allowSpecialFloats_) {
1229 token.type_ = tokenNaN;
1230 ok = match(
"aN", 2);
1236 if (features_.allowSpecialFloats_) {
1237 token.type_ = tokenPosInf;
1238 ok = match(
"nfinity", 7);
1244 token.type_ = tokenArraySeparator;
1247 token.type_ = tokenMemberSeparator;
1250 token.type_ = tokenEndOfStream;
1257 token.type_ = tokenError;
1258 token.end_ = current_;
1262void OurReader::skipSpaces() {
1263 while (current_ != end_) {
1265 if (c ==
' ' || c ==
'\t' || c ==
'\r' || c ==
'\n')
1272void OurReader::skipBom(
bool skipBom) {
1275 if ((end_ - begin_) >= 3 && strncmp(begin_,
"\xEF\xBB\xBF", 3) == 0) {
1282bool OurReader::match(
const Char* pattern,
int patternLength) {
1283 if (end_ - current_ < patternLength)
1285 int index = patternLength;
1287 if (current_[index] != pattern[index])
1289 current_ += patternLength;
1293bool OurReader::readComment() {
1294 const Location commentBegin = current_ - 1;
1295 const Char c = getNextChar();
1296 bool successful =
false;
1297 bool cStyleWithEmbeddedNewline =
false;
1299 const bool isCStyleComment = (c ==
'*');
1300 const bool isCppStyleComment = (c ==
'/');
1301 if (isCStyleComment) {
1302 successful = readCStyleComment(&cStyleWithEmbeddedNewline);
1303 }
else if (isCppStyleComment) {
1304 successful = readCppStyleComment();
1310 if (collectComments_) {
1313 if (!lastValueHasAComment_) {
1314 if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
1315 if (isCppStyleComment || !cStyleWithEmbeddedNewline) {
1317 lastValueHasAComment_ =
true;
1322 addComment(commentBegin, current_, placement);
1327String OurReader::normalizeEOL(OurReader::Location begin,
1328 OurReader::Location end) {
1330 normalized.reserve(
static_cast<size_t>(end - begin));
1331 OurReader::Location current = begin;
1332 while (current != end) {
1333 char c = *current++;
1335 if (current != end && *current ==
'\n')
1347void OurReader::addComment(Location begin, Location end,
1349 assert(collectComments_);
1350 const String& normalized = normalizeEOL(begin, end);
1352 assert(lastValue_ !=
nullptr);
1353 lastValue_->setComment(normalized, placement);
1355 commentsBefore_ += normalized;
1359bool OurReader::readCStyleComment(
bool* containsNewLineResult) {
1360 *containsNewLineResult =
false;
1362 while ((current_ + 1) < end_) {
1363 Char c = getNextChar();
1364 if (c ==
'*' && *current_ ==
'/')
1367 *containsNewLineResult =
true;
1370 return getNextChar() ==
'/';
1373bool OurReader::readCppStyleComment() {
1374 while (current_ != end_) {
1375 Char c = getNextChar();
1380 if (current_ != end_ && *current_ ==
'\n')
1389bool OurReader::readNumber(
bool checkInf) {
1390 Location p = current_;
1391 if (checkInf && p != end_ && *p ==
'I') {
1397 while (c >=
'0' && c <=
'9')
1398 c = (current_ = p) < end_ ? *p++ :
'\0';
1401 c = (current_ = p) < end_ ? *p++ :
'\0';
1402 while (c >=
'0' && c <=
'9')
1403 c = (current_ = p) < end_ ? *p++ :
'\0';
1406 if (c ==
'e' || c ==
'E') {
1407 c = (current_ = p) < end_ ? *p++ :
'\0';
1408 if (c ==
'+' || c ==
'-')
1409 c = (current_ = p) < end_ ? *p++ :
'\0';
1410 while (c >=
'0' && c <=
'9')
1411 c = (current_ = p) < end_ ? *p++ :
'\0';
1415bool OurReader::readString() {
1417 while (current_ != end_) {
1427bool OurReader::readStringSingleQuote() {
1429 while (current_ != end_) {
1439bool OurReader::readObject(Token& token) {
1443 currentValue().swapPayload(init);
1444 currentValue().setOffsetStart(token.start_ - begin_);
1445 while (readToken(tokenName)) {
1446 bool initialTokenOk =
true;
1447 while (tokenName.type_ == tokenComment && initialTokenOk)
1448 initialTokenOk = readToken(tokenName);
1449 if (!initialTokenOk)
1451 if (tokenName.type_ == tokenObjectEnd &&
1453 features_.allowTrailingCommas_))
1456 if (tokenName.type_ == tokenString) {
1457 if (!decodeString(tokenName, name))
1458 return recoverFromError(tokenObjectEnd);
1459 }
else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {
1461 if (!decodeNumber(tokenName, numberName))
1462 return recoverFromError(tokenObjectEnd);
1463 name = numberName.asString();
1467 if (name.length() >= (1U << 30))
1468 throwRuntimeError(
"keylength >= 2^30");
1469 if (features_.rejectDupKeys_ && currentValue().isMember(name)) {
1470 String msg =
"Duplicate key: '" + name +
"'";
1471 return addErrorAndRecover(msg, tokenName, tokenObjectEnd);
1475 if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
1476 return addErrorAndRecover(
"Missing ':' after object member name", colon,
1479 Value& value = currentValue()[name];
1480 nodes_.push(&value);
1481 bool ok = readValue();
1484 return recoverFromError(tokenObjectEnd);
1487 if (!readToken(comma) ||
1488 (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator &&
1489 comma.type_ != tokenComment)) {
1490 return addErrorAndRecover(
"Missing ',' or '}' in object declaration",
1491 comma, tokenObjectEnd);
1493 bool finalizeTokenOk =
true;
1494 while (comma.type_ == tokenComment && finalizeTokenOk)
1495 finalizeTokenOk = readToken(comma);
1496 if (comma.type_ == tokenObjectEnd)
1499 return addErrorAndRecover(
"Missing '}' or object member name", tokenName,
1503bool OurReader::readArray(Token& token) {
1505 currentValue().swapPayload(init);
1506 currentValue().setOffsetStart(token.start_ - begin_);
1510 if (current_ != end_ && *current_ ==
']' &&
1512 (features_.allowTrailingCommas_ &&
1513 !features_.allowDroppedNullPlaceholders_)))
1517 readToken(endArray);
1520 Value& value = currentValue()[index++];
1521 nodes_.push(&value);
1522 bool ok = readValue();
1525 return recoverFromError(tokenArrayEnd);
1529 ok = readToken(currentToken);
1530 while (currentToken.type_ == tokenComment && ok) {
1531 ok = readToken(currentToken);
1533 bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
1534 currentToken.type_ != tokenArrayEnd);
1535 if (!ok || badTokenType) {
1536 return addErrorAndRecover(
"Missing ',' or ']' in array declaration",
1537 currentToken, tokenArrayEnd);
1539 if (currentToken.type_ == tokenArrayEnd)
1545bool OurReader::decodeNumber(Token& token) {
1547 if (!decodeNumber(token, decoded))
1549 currentValue().swapPayload(decoded);
1550 currentValue().setOffsetStart(token.start_ - begin_);
1551 currentValue().setOffsetLimit(token.end_ - begin_);
1555bool OurReader::decodeNumber(Token& token, Value& decoded) {
1559 Location current = token.start_;
1560 const bool isNegative = *current ==
'-';
1569 "Int must be smaller than UInt");
1576 "The absolute value of minLargestInt must be greater than or "
1577 "equal to maxLargestInt");
1579 "The absolute value of minLargestInt must be only 1 magnitude "
1580 "larger than maxLargest Int");
1591 static constexpr auto negative_threshold =
1593 static constexpr auto negative_last_digit =
1597 isNegative ? negative_threshold : positive_threshold;
1599 isNegative ? negative_last_digit : positive_last_digit;
1602 while (current < token.end_) {
1603 Char c = *current++;
1604 if (c < '0' || c >
'9')
1605 return decodeDouble(token, decoded);
1607 const auto digit(
static_cast<Value::UInt>(c -
'0'));
1608 if (value >= threshold) {
1614 if (value > threshold || current != token.end_ ||
1615 digit > max_last_digit) {
1616 return decodeDouble(token, decoded);
1619 value = value * 10 + digit;
1624 const auto last_digit =
static_cast<Value::UInt>(value % 10);
1635bool OurReader::decodeDouble(Token& token) {
1637 if (!decodeDouble(token, decoded))
1639 currentValue().swapPayload(decoded);
1640 currentValue().setOffsetStart(token.start_ - begin_);
1641 currentValue().setOffsetLimit(token.end_ - begin_);
1645bool OurReader::decodeDouble(Token& token, Value& decoded) {
1647 const String buffer(token.start_, token.end_);
1649 if (!(is >> value)) {
1651 "'" +
String(token.start_, token.end_) +
"' is not a number.", token);
1657bool OurReader::decodeString(Token& token) {
1659 if (!decodeString(token, decoded_string))
1661 Value decoded(decoded_string);
1662 currentValue().swapPayload(decoded);
1663 currentValue().setOffsetStart(token.start_ - begin_);
1664 currentValue().setOffsetLimit(token.end_ - begin_);
1668bool OurReader::decodeString(Token& token,
String& decoded) {
1669 decoded.reserve(
static_cast<size_t>(token.end_ - token.start_ - 2));
1670 Location current = token.start_ + 1;
1671 Location end = token.end_ - 1;
1672 while (current != end) {
1673 Char c = *current++;
1678 return addError(
"Empty escape sequence in string", token, current);
1679 Char escape = *current++;
1706 unsigned int unicode;
1707 if (!decodeUnicodeCodePoint(token, current, end, unicode))
1712 return addError(
"Bad escape sequence in string", token, current);
1721bool OurReader::decodeUnicodeCodePoint(Token& token, Location& current,
1722 Location end,
unsigned int& unicode) {
1724 if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
1726 if (unicode >= 0xD800 && unicode <= 0xDBFF) {
1728 if (end - current < 6)
1730 "additional six characters expected to parse unicode surrogate pair.",
1732 if (*(current++) ==
'\\' && *(current++) ==
'u') {
1733 unsigned int surrogatePair;
1734 if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
1735 unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
1739 return addError(
"expecting another \\u token to begin the second half of "
1740 "a unicode surrogate pair",
1746bool OurReader::decodeUnicodeEscapeSequence(Token& token, Location& current,
1748 unsigned int& ret_unicode) {
1749 if (end - current < 4)
1751 "Bad unicode escape sequence in string: four digits expected.", token,
1754 for (
int index = 0; index < 4; ++index) {
1755 Char c = *current++;
1757 if (c >=
'0' && c <=
'9')
1759 else if (c >=
'a' && c <=
'f')
1760 unicode += c -
'a' + 10;
1761 else if (c >=
'A' && c <=
'F')
1762 unicode += c -
'A' + 10;
1765 "Bad unicode escape sequence in string: hexadecimal digit expected.",
1768 ret_unicode =
static_cast<unsigned int>(unicode);
1772bool OurReader::addError(
const String& message, Token& token, Location extra) {
1774 info.token_ = token;
1775 info.message_ = message;
1776 info.extra_ = extra;
1777 errors_.push_back(info);
1781bool OurReader::recoverFromError(TokenType skipUntilToken) {
1782 size_t errorCount = errors_.size();
1785 if (!readToken(skip))
1786 errors_.resize(errorCount);
1787 if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
1790 errors_.resize(errorCount);
1794bool OurReader::addErrorAndRecover(
const String& message, Token& token,
1795 TokenType skipUntilToken) {
1796 addError(message, token);
1797 return recoverFromError(skipUntilToken);
1800Value& OurReader::currentValue() {
return *(nodes_.top()); }
1802OurReader::Char OurReader::getNextChar() {
1803 if (current_ == end_)
1808void OurReader::getLocationLineAndColumn(Location location,
int& line,
1809 int& column)
const {
1810 Location current = begin_;
1811 Location lastLineStart = current;
1813 while (current < location && current != end_) {
1814 Char c = *current++;
1816 if (*current ==
'\n')
1818 lastLineStart = current;
1820 }
else if (c ==
'\n') {
1821 lastLineStart = current;
1826 column = int(location - lastLineStart) + 1;
1830String OurReader::getLocationLineAndColumn(Location location)
const {
1832 getLocationLineAndColumn(location, line, column);
1833 char buffer[18 + 16 + 16 + 1];
1834 jsoncpp_snprintf(buffer,
sizeof(buffer),
"Line %d, Column %d", line, column);
1838String OurReader::getFormattedErrorMessages()
const {
1840 for (
const auto& error : errors_) {
1842 "* " + getLocationLineAndColumn(error.token_.start_) +
"\n";
1843 formattedMessage +=
" " + error.message_ +
"\n";
1846 "See " + getLocationLineAndColumn(error.extra_) +
" for detail.\n";
1848 return formattedMessage;
1851std::vector<OurReader::StructuredError> OurReader::getStructuredErrors()
const {
1852 std::vector<OurReader::StructuredError> allErrors;
1853 for (
const auto& error : errors_) {
1854 OurReader::StructuredError structured;
1855 structured.offset_start = error.token_.start_ - begin_;
1856 structured.offset_limit = error.token_.end_ - begin_;
1857 structured.message = error.message_;
1858 allErrors.push_back(structured);
1863class OurCharReader :
public CharReader {
1864 bool const collectComments_;
1868 OurCharReader(
bool collectComments, OurFeatures
const& features)
1869 : collectComments_(collectComments), reader_(features) {}
1870 bool parse(
char const* beginDoc,
char const* endDoc, Value* root,
1872 bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
1874 *errs = reader_.getFormattedErrorMessages();
1884 OurFeatures features = OurFeatures::all();
1886 features.allowTrailingCommas_ =
settings_[
"allowTrailingCommas"].
asBool();
1888 features.allowDroppedNullPlaceholders_ =
1891 features.allowSingleQuotes_ =
settings_[
"allowSingleQuotes"].
asBool();
1895 features.stackLimit_ =
static_cast<size_t>(
settings_[
"stackLimit"].
asUInt());
1898 features.allowSpecialFloats_ =
settings_[
"allowSpecialFloats"].
asBool();
1900 return new OurCharReader(collectComments, features);
1904 static const auto& valid_keys = *
new std::set<String>{
1907 "allowTrailingCommas",
1909 "allowDroppedNullPlaceholders",
1911 "allowSingleQuotes",
1915 "allowSpecialFloats",
1919 auto key = si.name();
1920 if (valid_keys.count(key))
1923 (*invalid)[key] = *si;
1927 return invalid ? invalid->
empty() :
true;
1936 (*settings)[
"allowComments"] =
false;
1937 (*settings)[
"allowTrailingCommas"] =
false;
1938 (*settings)[
"strictRoot"] =
true;
1939 (*settings)[
"allowDroppedNullPlaceholders"] =
false;
1940 (*settings)[
"allowNumericKeys"] =
false;
1941 (*settings)[
"allowSingleQuotes"] =
false;
1942 (*settings)[
"stackLimit"] = 1000;
1943 (*settings)[
"failIfExtra"] =
true;
1944 (*settings)[
"rejectDupKeys"] =
true;
1945 (*settings)[
"allowSpecialFloats"] =
false;
1946 (*settings)[
"skipBom"] =
true;
1952 (*settings)[
"collectComments"] =
true;
1953 (*settings)[
"allowComments"] =
true;
1954 (*settings)[
"allowTrailingCommas"] =
true;
1955 (*settings)[
"strictRoot"] =
false;
1956 (*settings)[
"allowDroppedNullPlaceholders"] =
false;
1957 (*settings)[
"allowNumericKeys"] =
false;
1958 (*settings)[
"allowSingleQuotes"] =
false;
1959 (*settings)[
"stackLimit"] = 1000;
1960 (*settings)[
"failIfExtra"] =
false;
1961 (*settings)[
"rejectDupKeys"] =
false;
1962 (*settings)[
"allowSpecialFloats"] =
false;
1963 (*settings)[
"skipBom"] =
true;
1973 ssin << sin.rdbuf();
1975 char const* begin = doc.data();
1976 char const* end = begin + doc.size();
1979 return reader->parse(begin, end, root, errs);
1987 throwRuntimeError(errs);
virtual CharReader * newCharReader() const =0
Allocate a CharReader via operator new().
Build a CharReader implementation.
static void setDefaults(Json::Value *settings)
Called by ctor, but you can use this to reset settings_.
Value & operator[](const String &key)
A simple way to update a specific setting.
CharReader * newCharReader() const override
Allocate a CharReader via operator new().
static void strictMode(Json::Value *settings)
Same as old Features::strictMode().
Json::Value settings_
Configuration of this builder.
~CharReaderBuilder() override
bool validate(Json::Value *invalid) const
Interface for reading JSON from a char array.
Configuration passed to reader and writer.
bool strictRoot_
true if root must be either an array or an object value.
bool allowComments_
true if comments are allowed. Default: true.
bool allowDroppedNullPlaceholders_
true if dropped null placeholders are allowed. Default: false.
static Features all()
A configuration that allows all features and assumes all strings are UTF-8.
Features()
Initialize the configuration like JsonConfig::allFeatures;.
static Features strictMode()
A configuration that is strictly compatible with the JSON specification.
bool allowNumericKeys_
true if numeric object key are allowed. Default: false.
Reader()
Constructs a Reader allowing all features for parsing.
bool pushError(const Value &value, const String &message)
Add a semantic error message.
bool good() const
Return whether there are any errors.
std::vector< StructuredError > getStructuredErrors() const
Returns a vector of structured errors encountered while parsing.
bool parse(const std::string &document, Value &root, bool collectComments=true)
Read a Value from a JSON document.
String getFormattedErrorMessages() const
Returns a user friendly string that list errors in the parsed document.
const_iterator begin() const
bool empty() const
Return true if empty array, empty object, or null; otherwise, false.
static constexpr LargestInt maxLargestInt
Maximum signed integer value that can be stored in a Json::Value.
void setComment(const char *comment, size_t len, CommentPlacement placement)
Comments must be //... or /* ... */.
ptrdiff_t getOffsetLimit() const
const_iterator end() const
void swapPayload(Value &other)
Swap values but leave comments and source offsets in place.
void setOffsetLimit(ptrdiff_t limit)
Json::LargestInt LargestInt
Json::LargestUInt LargestUInt
void setOffsetStart(ptrdiff_t start)
static constexpr Int maxInt
Maximum signed int value that can be stored in a Json::Value.
static constexpr LargestUInt maxLargestUInt
Maximum unsigned integer value that can be stored in a Json::Value.
static constexpr LargestInt minLargestInt
Minimum signed integer value that can be stored in a Json::Value.
ptrdiff_t getOffsetStart() const
#define JSONCPP_DEPRECATED_STACK_LIMIT
static size_t const stackLimit_g
JSON (JavaScript Object Notation).
std::basic_istringstream< String::value_type, String::traits_type, String::allocator_type > IStringStream
std::basic_ostringstream< String::value_type, String::traits_type, String::allocator_type > OStringStream
std::basic_string< char, std::char_traits< char >, Allocator< char > > String
@ commentAfterOnSameLine
a comment just after a value on the same line
@ commentBefore
a comment placed on the line before a value
@ commentAfter
a comment on the line after a value (only make sense for
@ arrayValue
array value (ordered list)
@ objectValue
object value (collection of name/value pairs).
static String codePointToUTF8(unsigned int cp)
Converts a unicode code-point to UTF-8.
IStream & operator>>(IStream &, Value &)
Read from 'sin' into 'root'.
bool parseFromStream(CharReader::Factory const &, IStream &, Value *root, String *errs)
Consume entire stream and use its begin/end.
std::auto_ptr< CharReader > CharReaderPtr
An error tagged with where in the JSON text it was encountered.