7#if !defined(JSON_IS_AMALGAMATION)
26#if __cplusplus >= 201103L
29#define sscanf std::sscanf
35#if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES)
36#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
42#pragma warning(disable : 4996)
47#if !defined(JSONCPP_DEPRECATED_STACK_LIMIT)
48#define JSONCPP_DEPRECATED_STACK_LIMIT 1000
56#if __cplusplus >= 201103L || (defined(_CPPLIB_VER) && _CPPLIB_VER >= 520)
82 return std::any_of(begin, end, [](
char b) {
return b ==
'\n' || b ==
'\r'; });
93 bool collectComments) {
94 document_.assign(document.begin(), document.end());
95 const char* begin = document_.c_str();
96 const char* end = begin + document_.length();
97 return parse(begin, end, root, collectComments);
108 String doc(std::istreambuf_iterator<char>(is), {});
109 return parse(doc.data(), doc.data() + doc.size(), root, collectComments);
113 bool collectComments) {
115 collectComments =
false;
120 collectComments_ = collectComments;
122 lastValueEnd_ =
nullptr;
123 lastValue_ =
nullptr;
124 commentsBefore_.clear();
126 while (!nodes_.empty())
130 bool successful = readValue();
132 skipCommentTokens(token);
133 if (collectComments_ && !commentsBefore_.empty())
139 token.type_ = tokenError;
140 token.start_ = beginDoc;
143 "A valid JSON document must be either an array or an object value.",
151bool Reader::readValue() {
157 throwRuntimeError(
"Exceeded stackLimit in readValue().");
160 skipCommentTokens(token);
161 bool successful =
true;
163 if (collectComments_ && !commentsBefore_.empty()) {
165 commentsBefore_.clear();
168 switch (token.type_) {
169 case tokenObjectBegin:
170 successful = readObject(token);
173 case tokenArrayBegin:
174 successful = readArray(token);
178 successful = decodeNumber(token);
181 successful = decodeString(token);
201 case tokenArraySeparator:
217 return addError(
"Syntax error: value, object or array expected.", token);
220 if (collectComments_) {
221 lastValueEnd_ = current_;
222 lastValue_ = ¤tValue();
228void Reader::skipCommentTokens(Token& token) {
232 }
while (token.type_ == tokenComment);
238bool Reader::readToken(Token& token) {
240 token.start_ = current_;
241 Char c = getNextChar();
245 token.type_ = tokenObjectBegin;
248 token.type_ = tokenObjectEnd;
251 token.type_ = tokenArrayBegin;
254 token.type_ = tokenArrayEnd;
257 token.type_ = tokenString;
261 token.type_ = tokenComment;
275 token.type_ = tokenNumber;
279 token.type_ = tokenTrue;
280 ok = match(
"rue", 3);
283 token.type_ = tokenFalse;
284 ok = match(
"alse", 4);
287 token.type_ = tokenNull;
288 ok = match(
"ull", 3);
291 token.type_ = tokenArraySeparator;
294 token.type_ = tokenMemberSeparator;
297 token.type_ = tokenEndOfStream;
304 token.type_ = tokenError;
305 token.end_ = current_;
309void Reader::skipSpaces() {
310 while (current_ != end_) {
312 if (c ==
' ' || c ==
'\t' || c ==
'\r' || c ==
'\n')
319bool Reader::match(
const Char* pattern,
int patternLength) {
320 if (end_ - current_ < patternLength)
322 int index = patternLength;
324 if (current_[index] != pattern[index])
326 current_ += patternLength;
330bool Reader::readComment() {
331 Location commentBegin = current_ - 1;
332 Char c = getNextChar();
333 bool successful =
false;
335 successful = readCStyleComment();
337 successful = readCppStyleComment();
341 if (collectComments_) {
343 if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
344 if (c !=
'*' || !containsNewLine(commentBegin, current_))
348 addComment(commentBegin, current_, placement);
355 normalized.reserve(
static_cast<size_t>(end - begin));
357 while (current != end) {
360 if (current != end && *current ==
'\n')
372void Reader::addComment(Location begin, Location end,
373 CommentPlacement placement) {
374 assert(collectComments_);
375 const String& normalized = normalizeEOL(begin, end);
377 assert(lastValue_ !=
nullptr);
378 lastValue_->
setComment(normalized, placement);
380 commentsBefore_ += normalized;
384bool Reader::readCStyleComment() {
385 while ((current_ + 1) < end_) {
386 Char c = getNextChar();
387 if (c ==
'*' && *current_ ==
'/')
390 return getNextChar() ==
'/';
393bool Reader::readCppStyleComment() {
394 while (current_ != end_) {
395 Char c = getNextChar();
400 if (current_ != end_ && *current_ ==
'\n')
409void Reader::readNumber() {
413 while (c >=
'0' && c <=
'9')
414 c = (current_ = p) < end_ ? *p++ :
'\0';
417 c = (current_ = p) < end_ ? *p++ :
'\0';
418 while (c >=
'0' && c <=
'9')
419 c = (current_ = p) < end_ ? *p++ :
'\0';
422 if (c ==
'e' || c ==
'E') {
423 c = (current_ = p) < end_ ? *p++ :
'\0';
424 if (c ==
'+' || c ==
'-')
425 c = (current_ = p) < end_ ? *p++ :
'\0';
426 while (c >=
'0' && c <=
'9')
427 c = (current_ = p) < end_ ? *p++ :
'\0';
431bool Reader::readString() {
433 while (current_ != end_) {
443bool Reader::readObject(Token& token) {
449 while (readToken(tokenName)) {
450 bool initialTokenOk =
true;
451 while (tokenName.type_ == tokenComment && initialTokenOk)
452 initialTokenOk = readToken(tokenName);
455 if (tokenName.type_ == tokenObjectEnd && name.empty())
458 if (tokenName.type_ == tokenString) {
459 if (!decodeString(tokenName, name))
460 return recoverFromError(tokenObjectEnd);
463 if (!decodeNumber(tokenName, numberName))
464 return recoverFromError(tokenObjectEnd);
465 name = numberName.asString();
471 if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
472 return addErrorAndRecover(
"Missing ':' after object member name", colon,
475 Value& value = currentValue()[name];
477 bool ok = readValue();
480 return recoverFromError(tokenObjectEnd);
483 if (!readToken(comma) ||
484 (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator &&
485 comma.type_ != tokenComment)) {
486 return addErrorAndRecover(
"Missing ',' or '}' in object declaration",
487 comma, tokenObjectEnd);
489 bool finalizeTokenOk =
true;
490 while (comma.type_ == tokenComment && finalizeTokenOk)
491 finalizeTokenOk = readToken(comma);
492 if (comma.type_ == tokenObjectEnd)
495 return addErrorAndRecover(
"Missing '}' or object member name", tokenName,
499bool Reader::readArray(Token& token) {
504 if (current_ != end_ && *current_ ==
']')
512 Value& value = currentValue()[index++];
514 bool ok = readValue();
517 return recoverFromError(tokenArrayEnd);
521 ok = readToken(currentToken);
522 while (currentToken.type_ == tokenComment && ok) {
523 ok = readToken(currentToken);
525 bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
526 currentToken.type_ != tokenArrayEnd);
527 if (!ok || badTokenType) {
528 return addErrorAndRecover(
"Missing ',' or ']' in array declaration",
529 currentToken, tokenArrayEnd);
531 if (currentToken.type_ == tokenArrayEnd)
537bool Reader::decodeNumber(Token& token) {
539 if (!decodeNumber(token, decoded))
547bool Reader::decodeNumber(Token& token, Value& decoded) {
552 bool isNegative = *current ==
'-';
562 while (current < token.end_) {
564 if (c <
'0' || c >
'9')
565 return decodeDouble(token, decoded);
567 if (value >= threshold) {
572 if (value > threshold || current != token.end_ ||
573 digit > maxIntegerValue % 10) {
574 return decodeDouble(token, decoded);
577 value = value * 10 + digit;
579 if (isNegative && value == maxIntegerValue)
590bool Reader::decodeDouble(Token& token) {
592 if (!decodeDouble(token, decoded))
600bool Reader::decodeDouble(Token& token, Value& decoded) {
602 String buffer(token.start_, token.end_);
604 if (!(is >> value)) {
605 if (value == std::numeric_limits<double>::max())
606 value = std::numeric_limits<double>::infinity();
607 else if (value == std::numeric_limits<double>::lowest())
608 value = -std::numeric_limits<double>::infinity();
609 else if (!std::isinf(value))
611 "'" +
String(token.start_, token.end_) +
"' is not a number.", token);
617bool Reader::decodeString(Token& token) {
619 if (!decodeString(token, decoded_string))
621 Value decoded(decoded_string);
628bool Reader::decodeString(Token& token,
String& decoded) {
629 decoded.reserve(
static_cast<size_t>(token.end_ - token.start_ - 2));
630 Location current = token.start_ + 1;
632 while (current != end) {
638 return addError(
"Empty escape sequence in string", token, current);
639 Char escape = *current++;
666 unsigned int unicode;
667 if (!decodeUnicodeCodePoint(token, current, end, unicode))
672 return addError(
"Bad escape sequence in string", token, current);
681bool Reader::decodeUnicodeCodePoint(Token& token, Location& current,
682 Location end,
unsigned int& unicode) {
684 if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
686 if (unicode >= 0xD800 && unicode <= 0xDBFF) {
688 if (end - current < 6)
690 "additional six characters expected to parse unicode surrogate pair.",
692 if (*(current++) ==
'\\' && *(current++) ==
'u') {
693 unsigned int surrogatePair;
694 if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
695 unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
699 return addError(
"expecting another \\u token to begin the second half of "
700 "a unicode surrogate pair",
706bool Reader::decodeUnicodeEscapeSequence(Token& token, Location& current,
708 unsigned int& ret_unicode) {
709 if (end - current < 4)
711 "Bad unicode escape sequence in string: four digits expected.", token,
714 for (
int index = 0; index < 4; ++index) {
717 if (c >=
'0' && c <=
'9')
719 else if (c >=
'a' && c <=
'f')
720 unicode += c -
'a' + 10;
721 else if (c >=
'A' && c <=
'F')
722 unicode += c -
'A' + 10;
725 "Bad unicode escape sequence in string: hexadecimal digit expected.",
728 ret_unicode =
static_cast<unsigned int>(unicode);
732bool Reader::addError(
const String& message, Token& token, Location extra) {
735 info.message_ = message;
737 errors_.push_back(info);
741bool Reader::recoverFromError(TokenType skipUntilToken) {
742 size_t const errorCount = errors_.size();
745 if (!readToken(skip))
746 errors_.resize(errorCount);
747 if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
750 errors_.resize(errorCount);
754bool Reader::addErrorAndRecover(
const String& message, Token& token,
755 TokenType skipUntilToken) {
756 addError(message, token);
757 return recoverFromError(skipUntilToken);
760Value& Reader::currentValue() {
return *(nodes_.top()); }
763 if (current_ == end_)
768void Reader::getLocationLineAndColumn(Location location,
int& line,
773 while (current < location && current != end_) {
776 if (*current ==
'\n')
778 lastLineStart = current;
780 }
else if (c ==
'\n') {
781 lastLineStart = current;
786 column = int(location - lastLineStart) + 1;
790String Reader::getLocationLineAndColumn(Location location)
const {
792 getLocationLineAndColumn(location, line, column);
793 char buffer[18 + 16 + 16 + 1];
794 jsoncpp_snprintf(buffer,
sizeof(buffer),
"Line %d, Column %d", line, column);
799String Reader::getFormatedErrorMessages()
const {
805 for (
const auto& error : errors_) {
807 "* " + getLocationLineAndColumn(error.token_.start_) +
"\n";
808 formattedMessage +=
" " + error.message_ +
"\n";
811 "See " + getLocationLineAndColumn(error.extra_) +
" for detail.\n";
813 return formattedMessage;
817 std::vector<Reader::StructuredError> allErrors;
818 for (
const auto& error : errors_) {
822 structured.
message = error.message_;
823 allErrors.push_back(structured);
829 ptrdiff_t
const length = end_ - begin_;
833 token.type_ = tokenError;
838 info.message_ = message;
839 info.extra_ =
nullptr;
840 errors_.push_back(info);
845 const Value& extra) {
846 ptrdiff_t
const length = end_ - begin_;
851 token.type_ = tokenError;
856 info.message_ = message;
858 errors_.push_back(info);
868 static OurFeatures all();
870 bool allowTrailingCommas_;
872 bool allowDroppedNullPlaceholders_;
873 bool allowNumericKeys_;
874 bool allowSingleQuotes_;
877 bool allowSpecialFloats_;
882OurFeatures OurFeatures::all() {
return {}; }
892 using Location =
const Char*;
893 struct StructuredError {
894 ptrdiff_t offset_start;
895 ptrdiff_t offset_limit;
899 explicit OurReader(OurFeatures
const& features);
900 bool parse(
const char* beginDoc,
const char* endDoc, Value& root,
901 bool collectComments =
true);
902 String getFormattedErrorMessages()
const;
903 std::vector<StructuredError> getStructuredErrors()
const;
906 OurReader(OurReader
const&);
907 void operator=(OurReader
const&);
910 tokenEndOfStream = 0,
924 tokenMemberSeparator,
943 using Errors = std::deque<ErrorInfo>;
945 bool readToken(Token& token);
947 void skipBom(
bool skipBom);
948 bool match(
const Char* pattern,
int patternLength);
950 bool readCStyleComment(
bool* containsNewLineResult);
951 bool readCppStyleComment();
953 bool readStringSingleQuote();
954 bool readNumber(
bool checkInf);
956 bool readObject(Token& token);
957 bool readArray(Token& token);
958 bool decodeNumber(Token& token);
959 bool decodeNumber(Token& token, Value& decoded);
960 bool decodeString(Token& token);
961 bool decodeString(Token& token,
String& decoded);
962 bool decodeDouble(Token& token);
963 bool decodeDouble(Token& token, Value& decoded);
964 bool decodeUnicodeCodePoint(Token& token, Location& current, Location end,
965 unsigned int& unicode);
966 bool decodeUnicodeEscapeSequence(Token& token, Location& current,
967 Location end,
unsigned int& unicode);
968 bool addError(
const String& message, Token& token, Location extra =
nullptr);
969 bool recoverFromError(TokenType skipUntilToken);
970 bool addErrorAndRecover(
const String& message, Token& token,
971 TokenType skipUntilToken);
972 void skipUntilSpace();
973 Value& currentValue();
975 void getLocationLineAndColumn(Location location,
int& line,
977 String getLocationLineAndColumn(Location location)
const;
978 void addComment(Location begin, Location end, CommentPlacement placement);
979 void skipCommentTokens(Token& token);
981 static String normalizeEOL(Location begin, Location end);
982 static bool containsNewLine(Location begin, Location end);
984 using Nodes = std::stack<Value*>;
989 Location begin_ =
nullptr;
990 Location end_ =
nullptr;
991 Location current_ =
nullptr;
992 Location lastValueEnd_ =
nullptr;
993 Value* lastValue_ =
nullptr;
994 bool lastValueHasAComment_ =
false;
997 OurFeatures
const features_;
998 bool collectComments_ =
false;
1003bool OurReader::containsNewLine(OurReader::Location begin,
1004 OurReader::Location end) {
1005 return std::any_of(begin, end, [](
char b) {
return b ==
'\n' || b ==
'\r'; });
1008OurReader::OurReader(OurFeatures
const& features) : features_(features) {}
1010bool OurReader::parse(
const char* beginDoc,
const char* endDoc, Value& root,
1011 bool collectComments) {
1012 if (!features_.allowComments_) {
1013 collectComments =
false;
1018 collectComments_ = collectComments;
1020 lastValueEnd_ =
nullptr;
1021 lastValue_ =
nullptr;
1022 commentsBefore_.clear();
1024 while (!nodes_.empty())
1029 skipBom(features_.skipBom_);
1030 bool successful = readValue();
1033 skipCommentTokens(token);
1034 if (features_.failIfExtra_ && (token.type_ != tokenEndOfStream)) {
1035 addError(
"Extra non-whitespace after JSON value.", token);
1038 if (collectComments_ && !commentsBefore_.empty())
1040 if (features_.strictRoot_) {
1041 if (!root.isArray() && !root.isObject()) {
1044 token.type_ = tokenError;
1045 token.start_ = beginDoc;
1046 token.end_ = endDoc;
1048 "A valid JSON document must be either an array or an object value.",
1056bool OurReader::readValue() {
1058 if (nodes_.size() > features_.stackLimit_)
1059 throwRuntimeError(
"Exceeded stackLimit in readValue().");
1061 skipCommentTokens(token);
1062 bool successful =
true;
1064 if (collectComments_ && !commentsBefore_.empty()) {
1066 commentsBefore_.clear();
1069 switch (token.type_) {
1070 case tokenObjectBegin:
1071 successful = readObject(token);
1074 case tokenArrayBegin:
1075 successful = readArray(token);
1079 successful = decodeNumber(token);
1082 successful = decodeString(token);
1103 Value v(std::numeric_limits<double>::quiet_NaN());
1109 Value v(std::numeric_limits<double>::infinity());
1115 Value v(-std::numeric_limits<double>::infinity());
1120 case tokenArraySeparator:
1121 case tokenObjectEnd:
1123 if (features_.allowDroppedNullPlaceholders_) {
1136 return addError(
"Syntax error: value, object or array expected.", token);
1139 if (collectComments_) {
1140 lastValueEnd_ = current_;
1141 lastValueHasAComment_ =
false;
1142 lastValue_ = ¤tValue();
1148void OurReader::skipCommentTokens(Token& token) {
1149 if (features_.allowComments_) {
1152 }
while (token.type_ == tokenComment);
1158bool OurReader::readToken(Token& token) {
1160 token.start_ = current_;
1161 Char c = getNextChar();
1165 token.type_ = tokenObjectBegin;
1168 token.type_ = tokenObjectEnd;
1171 token.type_ = tokenArrayBegin;
1174 token.type_ = tokenArrayEnd;
1177 token.type_ = tokenString;
1181 if (features_.allowSingleQuotes_) {
1182 token.type_ = tokenString;
1183 ok = readStringSingleQuote();
1190 token.type_ = tokenComment;
1203 token.type_ = tokenNumber;
1207 if (readNumber(
true)) {
1208 token.type_ = tokenNumber;
1210 token.type_ = tokenNegInf;
1211 ok = features_.allowSpecialFloats_ && match(
"nfinity", 7);
1215 if (readNumber(
true)) {
1216 token.type_ = tokenNumber;
1218 token.type_ = tokenPosInf;
1219 ok = features_.allowSpecialFloats_ && match(
"nfinity", 7);
1223 token.type_ = tokenTrue;
1224 ok = match(
"rue", 3);
1227 token.type_ = tokenFalse;
1228 ok = match(
"alse", 4);
1231 token.type_ = tokenNull;
1232 ok = match(
"ull", 3);
1235 if (features_.allowSpecialFloats_) {
1236 token.type_ = tokenNaN;
1237 ok = match(
"aN", 2);
1243 if (features_.allowSpecialFloats_) {
1244 token.type_ = tokenPosInf;
1245 ok = match(
"nfinity", 7);
1251 token.type_ = tokenArraySeparator;
1254 token.type_ = tokenMemberSeparator;
1257 token.type_ = tokenEndOfStream;
1264 token.type_ = tokenError;
1265 token.end_ = current_;
1269void OurReader::skipSpaces() {
1270 while (current_ != end_) {
1272 if (c ==
' ' || c ==
'\t' || c ==
'\r' || c ==
'\n')
1279void OurReader::skipBom(
bool skipBom) {
1282 if ((end_ - begin_) >= 3 && strncmp(begin_,
"\xEF\xBB\xBF", 3) == 0) {
1289bool OurReader::match(
const Char* pattern,
int patternLength) {
1290 if (end_ - current_ < patternLength)
1292 int index = patternLength;
1294 if (current_[index] != pattern[index])
1296 current_ += patternLength;
1300bool OurReader::readComment() {
1301 const Location commentBegin = current_ - 1;
1302 const Char c = getNextChar();
1303 bool successful =
false;
1304 bool cStyleWithEmbeddedNewline =
false;
1306 const bool isCStyleComment = (c ==
'*');
1307 const bool isCppStyleComment = (c ==
'/');
1308 if (isCStyleComment) {
1309 successful = readCStyleComment(&cStyleWithEmbeddedNewline);
1310 }
else if (isCppStyleComment) {
1311 successful = readCppStyleComment();
1317 if (collectComments_) {
1320 if (!lastValueHasAComment_) {
1321 if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
1322 if (isCppStyleComment || !cStyleWithEmbeddedNewline) {
1324 lastValueHasAComment_ =
true;
1329 addComment(commentBegin, current_, placement);
1334String OurReader::normalizeEOL(OurReader::Location begin,
1335 OurReader::Location end) {
1337 normalized.reserve(
static_cast<size_t>(end - begin));
1338 OurReader::Location current = begin;
1339 while (current != end) {
1340 char c = *current++;
1342 if (current != end && *current ==
'\n')
1354void OurReader::addComment(Location begin, Location end,
1355 CommentPlacement placement) {
1356 assert(collectComments_);
1357 const String& normalized = normalizeEOL(begin, end);
1359 assert(lastValue_ !=
nullptr);
1360 lastValue_->
setComment(normalized, placement);
1362 commentsBefore_ += normalized;
1366bool OurReader::readCStyleComment(
bool* containsNewLineResult) {
1367 *containsNewLineResult =
false;
1369 while ((current_ + 1) < end_) {
1370 Char c = getNextChar();
1371 if (c ==
'*' && *current_ ==
'/')
1374 *containsNewLineResult =
true;
1377 return getNextChar() ==
'/';
1380bool OurReader::readCppStyleComment() {
1381 while (current_ != end_) {
1382 Char c = getNextChar();
1387 if (current_ != end_ && *current_ ==
'\n')
1396bool OurReader::readNumber(
bool checkInf) {
1397 Location p = current_;
1398 if (checkInf && p != end_ && *p ==
'I') {
1404 while (c >=
'0' && c <=
'9')
1405 c = (current_ = p) < end_ ? *p++ :
'\0';
1408 c = (current_ = p) < end_ ? *p++ :
'\0';
1409 while (c >=
'0' && c <=
'9')
1410 c = (current_ = p) < end_ ? *p++ :
'\0';
1413 if (c ==
'e' || c ==
'E') {
1414 c = (current_ = p) < end_ ? *p++ :
'\0';
1415 if (c ==
'+' || c ==
'-')
1416 c = (current_ = p) < end_ ? *p++ :
'\0';
1417 while (c >=
'0' && c <=
'9')
1418 c = (current_ = p) < end_ ? *p++ :
'\0';
1422bool OurReader::readString() {
1424 while (current_ != end_) {
1434bool OurReader::readStringSingleQuote() {
1436 while (current_ != end_) {
1446bool OurReader::readObject(Token& token) {
1452 while (readToken(tokenName)) {
1453 bool initialTokenOk =
true;
1454 while (tokenName.type_ == tokenComment && initialTokenOk)
1455 initialTokenOk = readToken(tokenName);
1456 if (!initialTokenOk)
1458 if (tokenName.type_ == tokenObjectEnd &&
1460 features_.allowTrailingCommas_))
1463 if (tokenName.type_ == tokenString) {
1464 if (!decodeString(tokenName, name))
1465 return recoverFromError(tokenObjectEnd);
1466 }
else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {
1468 if (!decodeNumber(tokenName, numberName))
1469 return recoverFromError(tokenObjectEnd);
1470 name = numberName.asString();
1474 if (name.length() >= (1U << 30))
1475 throwRuntimeError(
"keylength >= 2^30");
1476 if (features_.rejectDupKeys_ && currentValue().isMember(name)) {
1477 String msg =
"Duplicate key: '" + name +
"'";
1478 return addErrorAndRecover(msg, tokenName, tokenObjectEnd);
1482 if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
1483 return addErrorAndRecover(
"Missing ':' after object member name", colon,
1486 Value& value = currentValue()[name];
1487 nodes_.push(&value);
1488 bool ok = readValue();
1491 return recoverFromError(tokenObjectEnd);
1494 if (!readToken(comma) ||
1495 (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator &&
1496 comma.type_ != tokenComment)) {
1497 return addErrorAndRecover(
"Missing ',' or '}' in object declaration",
1498 comma, tokenObjectEnd);
1500 bool finalizeTokenOk =
true;
1501 while (comma.type_ == tokenComment && finalizeTokenOk)
1502 finalizeTokenOk = readToken(comma);
1503 if (comma.type_ == tokenObjectEnd)
1506 return addErrorAndRecover(
"Missing '}' or object member name", tokenName,
1510bool OurReader::readArray(Token& token) {
1517 if (current_ != end_ && *current_ ==
']' &&
1519 (features_.allowTrailingCommas_ &&
1520 !features_.allowDroppedNullPlaceholders_)))
1524 readToken(endArray);
1527 Value& value = currentValue()[index++];
1528 nodes_.push(&value);
1529 bool ok = readValue();
1532 return recoverFromError(tokenArrayEnd);
1536 ok = readToken(currentToken);
1537 while (currentToken.type_ == tokenComment && ok) {
1538 ok = readToken(currentToken);
1540 bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
1541 currentToken.type_ != tokenArrayEnd);
1542 if (!ok || badTokenType) {
1543 return addErrorAndRecover(
"Missing ',' or ']' in array declaration",
1544 currentToken, tokenArrayEnd);
1546 if (currentToken.type_ == tokenArrayEnd)
1552bool OurReader::decodeNumber(Token& token) {
1554 if (!decodeNumber(token, decoded))
1562bool OurReader::decodeNumber(Token& token, Value& decoded) {
1566 Location current = token.start_;
1567 const bool isNegative = *current ==
'-';
1576 "Int must be smaller than UInt");
1583 "The absolute value of minLargestInt must be greater than or "
1584 "equal to maxLargestInt");
1586 "The absolute value of minLargestInt must be only 1 magnitude "
1587 "larger than maxLargest Int");
1598 static constexpr auto negative_threshold =
1600 static constexpr auto negative_last_digit =
1604 isNegative ? negative_threshold : positive_threshold;
1606 isNegative ? negative_last_digit : positive_last_digit;
1609 while (current < token.end_) {
1610 Char c = *current++;
1611 if (c <
'0' || c >
'9')
1612 return decodeDouble(token, decoded);
1614 const auto digit(
static_cast<Value::UInt>(c -
'0'));
1615 if (value >= threshold) {
1621 if (value > threshold || current != token.end_ ||
1622 digit > max_last_digit) {
1623 return decodeDouble(token, decoded);
1626 value = value * 10 + digit;
1631 const auto last_digit =
static_cast<Value::UInt>(value % 10);
1642bool OurReader::decodeDouble(Token& token) {
1644 if (!decodeDouble(token, decoded))
1652bool OurReader::decodeDouble(Token& token, Value& decoded) {
1654 const String buffer(token.start_, token.end_);
1656 if (!(is >> value)) {
1657 if (value == std::numeric_limits<double>::max())
1658 value = std::numeric_limits<double>::infinity();
1659 else if (value == std::numeric_limits<double>::lowest())
1660 value = -std::numeric_limits<double>::infinity();
1661 else if (!std::isinf(value))
1663 "'" +
String(token.start_, token.end_) +
"' is not a number.", token);
1669bool OurReader::decodeString(Token& token) {
1671 if (!decodeString(token, decoded_string))
1673 Value decoded(decoded_string);
1680bool OurReader::decodeString(Token& token,
String& decoded) {
1681 decoded.reserve(
static_cast<size_t>(token.end_ - token.start_ - 2));
1682 Location current = token.start_ + 1;
1683 Location end = token.end_ - 1;
1684 while (current != end) {
1685 Char c = *current++;
1690 return addError(
"Empty escape sequence in string", token, current);
1691 Char escape = *current++;
1718 unsigned int unicode;
1719 if (!decodeUnicodeCodePoint(token, current, end, unicode))
1724 return addError(
"Bad escape sequence in string", token, current);
1733bool OurReader::decodeUnicodeCodePoint(Token& token, Location& current,
1734 Location end,
unsigned int& unicode) {
1736 if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
1738 if (unicode >= 0xD800 && unicode <= 0xDBFF) {
1740 if (end - current < 6)
1742 "additional six characters expected to parse unicode surrogate pair.",
1744 if (*(current++) ==
'\\' && *(current++) ==
'u') {
1745 unsigned int surrogatePair;
1746 if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
1747 unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
1751 return addError(
"expecting another \\u token to begin the second half of "
1752 "a unicode surrogate pair",
1758bool OurReader::decodeUnicodeEscapeSequence(Token& token, Location& current,
1760 unsigned int& ret_unicode) {
1761 if (end - current < 4)
1763 "Bad unicode escape sequence in string: four digits expected.", token,
1766 for (
int index = 0; index < 4; ++index) {
1767 Char c = *current++;
1769 if (c >=
'0' && c <=
'9')
1771 else if (c >=
'a' && c <=
'f')
1772 unicode += c -
'a' + 10;
1773 else if (c >=
'A' && c <=
'F')
1774 unicode += c -
'A' + 10;
1777 "Bad unicode escape sequence in string: hexadecimal digit expected.",
1780 ret_unicode =
static_cast<unsigned int>(unicode);
1784bool OurReader::addError(
const String& message, Token& token, Location extra) {
1786 info.token_ = token;
1787 info.message_ = message;
1788 info.extra_ = extra;
1789 errors_.push_back(info);
1793bool OurReader::recoverFromError(TokenType skipUntilToken) {
1794 size_t errorCount = errors_.size();
1797 if (!readToken(skip))
1798 errors_.resize(errorCount);
1799 if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
1802 errors_.resize(errorCount);
1806bool OurReader::addErrorAndRecover(
const String& message, Token& token,
1807 TokenType skipUntilToken) {
1808 addError(message, token);
1809 return recoverFromError(skipUntilToken);
1812Value& OurReader::currentValue() {
return *(nodes_.top()); }
1814OurReader::Char OurReader::getNextChar() {
1815 if (current_ == end_)
1820void OurReader::getLocationLineAndColumn(Location location,
int& line,
1821 int& column)
const {
1822 Location current = begin_;
1823 Location lastLineStart = current;
1825 while (current < location && current != end_) {
1826 Char c = *current++;
1828 if (*current ==
'\n')
1830 lastLineStart = current;
1832 }
else if (c ==
'\n') {
1833 lastLineStart = current;
1838 column = int(location - lastLineStart) + 1;
1842String OurReader::getLocationLineAndColumn(Location location)
const {
1844 getLocationLineAndColumn(location, line, column);
1845 char buffer[18 + 16 + 16 + 1];
1846 jsoncpp_snprintf(buffer,
sizeof(buffer),
"Line %d, Column %d", line, column);
1850String OurReader::getFormattedErrorMessages()
const {
1852 for (
const auto& error : errors_) {
1854 "* " + getLocationLineAndColumn(error.token_.start_) +
"\n";
1855 formattedMessage +=
" " + error.message_ +
"\n";
1858 "See " + getLocationLineAndColumn(error.extra_) +
" for detail.\n";
1860 return formattedMessage;
1863std::vector<OurReader::StructuredError> OurReader::getStructuredErrors()
const {
1864 std::vector<OurReader::StructuredError> allErrors;
1865 for (
const auto& error : errors_) {
1866 OurReader::StructuredError structured;
1867 structured.offset_start = error.token_.start_ - begin_;
1868 structured.offset_limit = error.token_.end_ - begin_;
1869 structured.message = error.message_;
1870 allErrors.push_back(structured);
1875class OurCharReader :
public CharReader {
1876 bool const collectComments_;
1880 OurCharReader(
bool collectComments, OurFeatures
const& features)
1881 : collectComments_(collectComments), reader_(features) {}
1882 bool parse(
char const* beginDoc,
char const* endDoc, Value* root,
1884 bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
1886 *errs = reader_.getFormattedErrorMessages();
1896 OurFeatures features = OurFeatures::all();
1898 features.allowTrailingCommas_ =
settings_[
"allowTrailingCommas"].
asBool();
1900 features.allowDroppedNullPlaceholders_ =
1903 features.allowSingleQuotes_ =
settings_[
"allowSingleQuotes"].
asBool();
1907 features.stackLimit_ =
static_cast<size_t>(
settings_[
"stackLimit"].
asUInt());
1910 features.allowSpecialFloats_ =
settings_[
"allowSpecialFloats"].
asBool();
1912 return new OurCharReader(collectComments, features);
1916 static const auto& valid_keys = *
new std::set<String>{
1919 "allowTrailingCommas",
1921 "allowDroppedNullPlaceholders",
1923 "allowSingleQuotes",
1927 "allowSpecialFloats",
1931 auto key = si.name();
1932 if (valid_keys.count(key))
1935 (*invalid)[key] = *si;
1939 return invalid ? invalid->
empty() :
true;
1948 (*settings)[
"allowComments"] =
false;
1949 (*settings)[
"allowTrailingCommas"] =
false;
1950 (*settings)[
"strictRoot"] =
true;
1951 (*settings)[
"allowDroppedNullPlaceholders"] =
false;
1952 (*settings)[
"allowNumericKeys"] =
false;
1953 (*settings)[
"allowSingleQuotes"] =
false;
1954 (*settings)[
"stackLimit"] = 1000;
1955 (*settings)[
"failIfExtra"] =
true;
1956 (*settings)[
"rejectDupKeys"] =
true;
1957 (*settings)[
"allowSpecialFloats"] =
false;
1958 (*settings)[
"skipBom"] =
true;
1964 (*settings)[
"collectComments"] =
true;
1965 (*settings)[
"allowComments"] =
true;
1966 (*settings)[
"allowTrailingCommas"] =
true;
1967 (*settings)[
"strictRoot"] =
false;
1968 (*settings)[
"allowDroppedNullPlaceholders"] =
false;
1969 (*settings)[
"allowNumericKeys"] =
false;
1970 (*settings)[
"allowSingleQuotes"] =
false;
1971 (*settings)[
"stackLimit"] = 1000;
1972 (*settings)[
"failIfExtra"] =
false;
1973 (*settings)[
"rejectDupKeys"] =
false;
1974 (*settings)[
"allowSpecialFloats"] =
false;
1975 (*settings)[
"skipBom"] =
true;
1985 ssin << sin.rdbuf();
1987 char const* begin = doc.data();
1988 char const* end = begin + doc.size();
1991 return reader->parse(begin, end, root, errs);
1999 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_ostringstream< String::value_type, String::traits_type, String::allocator_type > OStringStream
@ 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
std::basic_istringstream< String::value_type, String::traits_type, String::allocator_type > IStringStream
@ 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.
std::auto_ptr< CharReader > CharReaderPtr
std::basic_string< char, std::char_traits< char >, Allocator< char > > String
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.
An error tagged with where in the JSON text it was encountered.