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 readTokenSkippingComments(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 readTokenSkippingComments(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();
228bool Reader::readTokenSkippingComments(Token& token) {
229 bool success = readToken(token);
231 while (success && token.type_ == tokenComment) {
232 success = readToken(token);
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,
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 (readTokenSkippingComments(tokenName)) {
450 if (tokenName.type_ == tokenObjectEnd && name.empty())
453 if (tokenName.type_ == tokenString) {
454 if (!decodeString(tokenName, name))
455 return recoverFromError(tokenObjectEnd);
458 if (!decodeNumber(tokenName, numberName))
459 return recoverFromError(tokenObjectEnd);
460 name = numberName.asString();
466 if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
467 return addErrorAndRecover(
"Missing ':' after object member name", colon,
470 Value& value = currentValue()[name];
472 bool ok = readValue();
475 return recoverFromError(tokenObjectEnd);
478 if (!readTokenSkippingComments(comma) ||
479 (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator)) {
480 return addErrorAndRecover(
"Missing ',' or '}' in object declaration",
481 comma, tokenObjectEnd);
483 if (comma.type_ == tokenObjectEnd)
486 return addErrorAndRecover(
"Missing '}' or object member name", tokenName,
490bool Reader::readArray(Token& token) {
495 if (current_ != end_ && *current_ ==
']')
503 Value& value = currentValue()[index++];
505 bool ok = readValue();
508 return recoverFromError(tokenArrayEnd);
512 ok = readTokenSkippingComments(currentToken);
513 bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
514 currentToken.type_ != tokenArrayEnd);
515 if (!ok || badTokenType) {
516 return addErrorAndRecover(
"Missing ',' or ']' in array declaration",
517 currentToken, tokenArrayEnd);
519 if (currentToken.type_ == tokenArrayEnd)
525bool Reader::decodeNumber(Token& token) {
527 if (!decodeNumber(token, decoded))
535bool Reader::decodeNumber(Token& token, Value& decoded) {
540 bool isNegative = *current ==
'-';
550 while (current < token.end_) {
552 if (c <
'0' || c >
'9')
553 return decodeDouble(token, decoded);
555 if (value >= threshold) {
560 if (value > threshold || current != token.end_ ||
561 digit > maxIntegerValue % 10) {
562 return decodeDouble(token, decoded);
565 value = value * 10 + digit;
567 if (isNegative && value == maxIntegerValue)
578bool Reader::decodeDouble(Token& token) {
580 if (!decodeDouble(token, decoded))
588bool Reader::decodeDouble(Token& token, Value& decoded) {
591 if (!(is >> value)) {
592 if (value == std::numeric_limits<double>::max())
593 value = std::numeric_limits<double>::infinity();
594 else if (value == std::numeric_limits<double>::lowest())
595 value = -std::numeric_limits<double>::infinity();
596 else if (!std::isinf(value))
598 "'" +
String(token.start_, token.end_) +
"' is not a number.", token);
604bool Reader::decodeString(Token& token) {
606 if (!decodeString(token, decoded_string))
608 Value decoded(decoded_string);
615bool Reader::decodeString(Token& token,
String& decoded) {
616 decoded.reserve(
static_cast<size_t>(token.end_ - token.start_ - 2));
617 Location current = token.start_ + 1;
619 while (current != end) {
625 return addError(
"Empty escape sequence in string", token, current);
626 Char escape = *current++;
653 unsigned int unicode;
654 if (!decodeUnicodeCodePoint(token, current, end, unicode))
659 return addError(
"Bad escape sequence in string", token, current);
668bool Reader::decodeUnicodeCodePoint(Token& token, Location& current,
669 Location end,
unsigned int& unicode) {
671 if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
673 if (unicode >= 0xD800 && unicode <= 0xDBFF) {
675 if (end - current < 6)
677 "additional six characters expected to parse unicode surrogate pair.",
679 if (*(current++) ==
'\\' && *(current++) ==
'u') {
680 unsigned int surrogatePair;
681 if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
682 unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
686 return addError(
"expecting another \\u token to begin the second half of "
687 "a unicode surrogate pair",
693bool Reader::decodeUnicodeEscapeSequence(Token& token, Location& current,
695 unsigned int& ret_unicode) {
696 if (end - current < 4)
698 "Bad unicode escape sequence in string: four digits expected.", token,
701 for (
int index = 0; index < 4; ++index) {
704 if (c >=
'0' && c <=
'9')
706 else if (c >=
'a' && c <=
'f')
707 unicode += c -
'a' + 10;
708 else if (c >=
'A' && c <=
'F')
709 unicode += c -
'A' + 10;
712 "Bad unicode escape sequence in string: hexadecimal digit expected.",
715 ret_unicode =
static_cast<unsigned int>(unicode);
719bool Reader::addError(
const String& message, Token& token, Location extra) {
722 info.message_ = message;
724 errors_.push_back(info);
728bool Reader::recoverFromError(TokenType skipUntilToken) {
729 size_t const errorCount = errors_.size();
732 if (!readToken(skip))
733 errors_.resize(errorCount);
734 if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
737 errors_.resize(errorCount);
741bool Reader::addErrorAndRecover(
const String& message, Token& token,
742 TokenType skipUntilToken) {
743 addError(message, token);
744 return recoverFromError(skipUntilToken);
747Value& Reader::currentValue() {
return *(nodes_.top()); }
750 if (current_ == end_)
755void Reader::getLocationLineAndColumn(Location location,
int& line,
760 while (current < location && current != end_) {
763 if (current != end_ && *current ==
'\n')
765 lastLineStart = current;
767 }
else if (c ==
'\n') {
768 lastLineStart = current;
773 column = int(location - lastLineStart) + 1;
777String Reader::getLocationLineAndColumn(Location location)
const {
779 getLocationLineAndColumn(location, line, column);
780 char buffer[18 + 16 + 16 + 1];
781 jsoncpp_snprintf(buffer,
sizeof(buffer),
"Line %d, Column %d", line, column);
786String Reader::getFormatedErrorMessages()
const {
792 for (
const auto& error : errors_) {
794 "* " + getLocationLineAndColumn(error.token_.start_) +
"\n";
795 formattedMessage +=
" " + error.message_ +
"\n";
798 "See " + getLocationLineAndColumn(error.extra_) +
" for detail.\n";
800 return formattedMessage;
804 std::vector<Reader::StructuredError> allErrors;
805 for (
const auto& error : errors_) {
809 structured.
message = error.message_;
810 allErrors.push_back(structured);
816 ptrdiff_t
const length = end_ - begin_;
820 token.type_ = tokenError;
825 info.message_ = message;
826 info.extra_ =
nullptr;
827 errors_.push_back(info);
832 const Value& extra) {
833 ptrdiff_t
const length = end_ - begin_;
838 token.type_ = tokenError;
843 info.message_ = message;
845 errors_.push_back(info);
855 static OurFeatures all();
857 bool allowTrailingCommas_;
859 bool allowDroppedNullPlaceholders_;
860 bool allowNumericKeys_;
861 bool allowSingleQuotes_;
864 bool allowSpecialFloats_;
869OurFeatures OurFeatures::all() {
return {}; }
879 using Location =
const Char*;
881 explicit OurReader(OurFeatures
const& features);
882 bool parse(
const char* beginDoc,
const char* endDoc, Value& root,
883 bool collectComments =
true);
884 String getFormattedErrorMessages()
const;
885 std::vector<CharReader::StructuredError> getStructuredErrors()
const;
888 OurReader(OurReader
const&);
889 void operator=(OurReader
const&);
892 tokenEndOfStream = 0,
906 tokenMemberSeparator,
925 using Errors = std::deque<ErrorInfo>;
927 bool readToken(Token& token);
928 bool readTokenSkippingComments(Token& token);
930 void skipBom(
bool skipBom);
931 bool match(
const Char* pattern,
int patternLength);
933 bool readCStyleComment(
bool* containsNewLineResult);
934 bool readCppStyleComment();
936 bool readStringSingleQuote();
937 bool readNumber(
bool checkInf);
939 bool readObject(Token& token);
940 bool readArray(Token& token);
941 bool decodeNumber(Token& token);
942 bool decodeNumber(Token& token, Value& decoded);
943 bool decodeString(Token& token);
944 bool decodeString(Token& token,
String& decoded);
945 bool decodeDouble(Token& token);
946 bool decodeDouble(Token& token, Value& decoded);
947 bool decodeUnicodeCodePoint(Token& token, Location& current, Location end,
948 unsigned int& unicode);
949 bool decodeUnicodeEscapeSequence(Token& token, Location& current,
950 Location end,
unsigned int& unicode);
951 bool addError(
const String& message, Token& token, Location extra =
nullptr);
952 bool recoverFromError(TokenType skipUntilToken);
953 bool addErrorAndRecover(
const String& message, Token& token,
954 TokenType skipUntilToken);
955 void skipUntilSpace();
956 Value& currentValue();
958 void getLocationLineAndColumn(Location location,
int& line,
960 String getLocationLineAndColumn(Location location)
const;
963 static String normalizeEOL(Location begin, Location end);
964 static bool containsNewLine(Location begin, Location end);
966 using Nodes = std::stack<Value*>;
971 Location begin_ =
nullptr;
972 Location end_ =
nullptr;
973 Location current_ =
nullptr;
974 Location lastValueEnd_ =
nullptr;
975 Value* lastValue_ =
nullptr;
976 bool lastValueHasAComment_ =
false;
979 OurFeatures
const features_;
980 bool collectComments_ =
false;
985bool OurReader::containsNewLine(OurReader::Location begin,
986 OurReader::Location end) {
987 return std::any_of(begin, end, [](
char b) {
return b ==
'\n' || b ==
'\r'; });
990OurReader::OurReader(OurFeatures
const& features) : features_(features) {}
992bool OurReader::parse(
const char* beginDoc,
const char* endDoc, Value& root,
993 bool collectComments) {
994 if (!features_.allowComments_) {
995 collectComments =
false;
1000 collectComments_ = collectComments;
1002 lastValueEnd_ =
nullptr;
1003 lastValue_ =
nullptr;
1004 commentsBefore_.clear();
1006 while (!nodes_.empty())
1011 skipBom(features_.skipBom_);
1012 bool successful = readValue();
1015 readTokenSkippingComments(token);
1016 if (features_.failIfExtra_ && (token.type_ != tokenEndOfStream)) {
1017 addError(
"Extra non-whitespace after JSON value.", token);
1020 if (collectComments_ && !commentsBefore_.empty())
1022 if (features_.strictRoot_) {
1023 if (!root.isArray() && !root.isObject()) {
1026 token.type_ = tokenError;
1027 token.start_ = beginDoc;
1028 token.end_ = endDoc;
1030 "A valid JSON document must be either an array or an object value.",
1038bool OurReader::readValue() {
1040 if (nodes_.size() > features_.stackLimit_)
1041 throwRuntimeError(
"Exceeded stackLimit in readValue().");
1043 readTokenSkippingComments(token);
1044 bool successful =
true;
1046 if (collectComments_ && !commentsBefore_.empty()) {
1048 commentsBefore_.clear();
1051 switch (token.type_) {
1052 case tokenObjectBegin:
1053 successful = readObject(token);
1054 currentValue().setOffsetLimit(current_ - begin_);
1056 case tokenArrayBegin:
1057 successful = readArray(token);
1058 currentValue().setOffsetLimit(current_ - begin_);
1061 successful = decodeNumber(token);
1064 successful = decodeString(token);
1068 currentValue().swapPayload(v);
1069 currentValue().setOffsetStart(token.start_ - begin_);
1070 currentValue().setOffsetLimit(token.end_ - begin_);
1074 currentValue().swapPayload(v);
1075 currentValue().setOffsetStart(token.start_ - begin_);
1076 currentValue().setOffsetLimit(token.end_ - begin_);
1080 currentValue().swapPayload(v);
1081 currentValue().setOffsetStart(token.start_ - begin_);
1082 currentValue().setOffsetLimit(token.end_ - begin_);
1085 Value v(std::numeric_limits<double>::quiet_NaN());
1086 currentValue().swapPayload(v);
1087 currentValue().setOffsetStart(token.start_ - begin_);
1088 currentValue().setOffsetLimit(token.end_ - begin_);
1091 Value v(std::numeric_limits<double>::infinity());
1092 currentValue().swapPayload(v);
1093 currentValue().setOffsetStart(token.start_ - begin_);
1094 currentValue().setOffsetLimit(token.end_ - begin_);
1097 Value v(-std::numeric_limits<double>::infinity());
1098 currentValue().swapPayload(v);
1099 currentValue().setOffsetStart(token.start_ - begin_);
1100 currentValue().setOffsetLimit(token.end_ - begin_);
1102 case tokenArraySeparator:
1103 case tokenObjectEnd:
1105 if (features_.allowDroppedNullPlaceholders_) {
1110 currentValue().swapPayload(v);
1111 currentValue().setOffsetStart(current_ - begin_ - 1);
1112 currentValue().setOffsetLimit(current_ - begin_);
1116 currentValue().setOffsetStart(token.start_ - begin_);
1117 currentValue().setOffsetLimit(token.end_ - begin_);
1118 return addError(
"Syntax error: value, object or array expected.", token);
1121 if (collectComments_) {
1122 lastValueEnd_ = current_;
1123 lastValueHasAComment_ =
false;
1124 lastValue_ = ¤tValue();
1130bool OurReader::readTokenSkippingComments(Token& token) {
1131 bool success = readToken(token);
1132 if (features_.allowComments_) {
1133 while (success && token.type_ == tokenComment) {
1134 success = readToken(token);
1140bool OurReader::readToken(Token& token) {
1142 token.start_ = current_;
1143 Char c = getNextChar();
1147 token.type_ = tokenObjectBegin;
1150 token.type_ = tokenObjectEnd;
1153 token.type_ = tokenArrayBegin;
1156 token.type_ = tokenArrayEnd;
1159 token.type_ = tokenString;
1163 if (features_.allowSingleQuotes_) {
1164 token.type_ = tokenString;
1165 ok = readStringSingleQuote();
1172 token.type_ = tokenComment;
1185 token.type_ = tokenNumber;
1189 if (readNumber(
true)) {
1190 token.type_ = tokenNumber;
1192 token.type_ = tokenNegInf;
1193 ok = features_.allowSpecialFloats_ && match(
"nfinity", 7);
1197 if (readNumber(
true)) {
1198 token.type_ = tokenNumber;
1200 token.type_ = tokenPosInf;
1201 ok = features_.allowSpecialFloats_ && match(
"nfinity", 7);
1205 token.type_ = tokenTrue;
1206 ok = match(
"rue", 3);
1209 token.type_ = tokenFalse;
1210 ok = match(
"alse", 4);
1213 token.type_ = tokenNull;
1214 ok = match(
"ull", 3);
1217 if (features_.allowSpecialFloats_) {
1218 token.type_ = tokenNaN;
1219 ok = match(
"aN", 2);
1225 if (features_.allowSpecialFloats_) {
1226 token.type_ = tokenPosInf;
1227 ok = match(
"nfinity", 7);
1233 token.type_ = tokenArraySeparator;
1236 token.type_ = tokenMemberSeparator;
1239 token.type_ = tokenEndOfStream;
1246 token.type_ = tokenError;
1247 token.end_ = current_;
1251void OurReader::skipSpaces() {
1252 while (current_ != end_) {
1254 if (c ==
' ' || c ==
'\t' || c ==
'\r' || c ==
'\n')
1261void OurReader::skipBom(
bool skipBom) {
1264 if ((end_ - begin_) >= 3 && strncmp(begin_,
"\xEF\xBB\xBF", 3) == 0) {
1271bool OurReader::match(
const Char* pattern,
int patternLength) {
1272 if (end_ - current_ < patternLength)
1274 int index = patternLength;
1276 if (current_[index] != pattern[index])
1278 current_ += patternLength;
1282bool OurReader::readComment() {
1283 const Location commentBegin = current_ - 1;
1284 const Char c = getNextChar();
1285 bool successful =
false;
1286 bool cStyleWithEmbeddedNewline =
false;
1288 const bool isCStyleComment = (c ==
'*');
1289 const bool isCppStyleComment = (c ==
'/');
1290 if (isCStyleComment) {
1291 successful = readCStyleComment(&cStyleWithEmbeddedNewline);
1292 }
else if (isCppStyleComment) {
1293 successful = readCppStyleComment();
1299 if (collectComments_) {
1302 if (!lastValueHasAComment_) {
1303 if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
1304 if (isCppStyleComment || !cStyleWithEmbeddedNewline) {
1306 lastValueHasAComment_ =
true;
1311 addComment(commentBegin, current_, placement);
1316String OurReader::normalizeEOL(OurReader::Location begin,
1317 OurReader::Location end) {
1319 normalized.reserve(
static_cast<size_t>(end - begin));
1320 OurReader::Location current = begin;
1321 while (current != end) {
1322 char c = *current++;
1324 if (current != end && *current ==
'\n')
1336void OurReader::addComment(Location begin, Location end,
1338 assert(collectComments_);
1339 const String& normalized = normalizeEOL(begin, end);
1341 assert(lastValue_ !=
nullptr);
1342 lastValue_->setComment(normalized, placement);
1344 commentsBefore_ += normalized;
1348bool OurReader::readCStyleComment(
bool* containsNewLineResult) {
1349 *containsNewLineResult =
false;
1351 while ((current_ + 1) < end_) {
1352 Char c = getNextChar();
1353 if (c ==
'*' && *current_ ==
'/')
1356 *containsNewLineResult =
true;
1359 return getNextChar() ==
'/';
1362bool OurReader::readCppStyleComment() {
1363 while (current_ != end_) {
1364 Char c = getNextChar();
1369 if (current_ != end_ && *current_ ==
'\n')
1378bool OurReader::readNumber(
bool checkInf) {
1379 Location p = current_;
1380 if (checkInf && p != end_ && *p ==
'I') {
1386 while (c >=
'0' && c <=
'9')
1387 c = (current_ = p) < end_ ? *p++ :
'\0';
1390 c = (current_ = p) < end_ ? *p++ :
'\0';
1391 while (c >=
'0' && c <=
'9')
1392 c = (current_ = p) < end_ ? *p++ :
'\0';
1395 if (c ==
'e' || c ==
'E') {
1396 c = (current_ = p) < end_ ? *p++ :
'\0';
1397 if (c ==
'+' || c ==
'-')
1398 c = (current_ = p) < end_ ? *p++ :
'\0';
1399 while (c >=
'0' && c <=
'9')
1400 c = (current_ = p) < end_ ? *p++ :
'\0';
1404bool OurReader::readString() {
1406 while (current_ != end_) {
1416bool OurReader::readStringSingleQuote() {
1418 while (current_ != end_) {
1428bool OurReader::readObject(Token& token) {
1432 currentValue().swapPayload(init);
1433 currentValue().setOffsetStart(token.start_ - begin_);
1434 while (readTokenSkippingComments(tokenName)) {
1435 if (tokenName.type_ == tokenObjectEnd &&
1437 features_.allowTrailingCommas_))
1440 if (tokenName.type_ == tokenString) {
1441 if (!decodeString(tokenName, name))
1442 return recoverFromError(tokenObjectEnd);
1443 }
else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {
1445 if (!decodeNumber(tokenName, numberName))
1446 return recoverFromError(tokenObjectEnd);
1447 name = numberName.asString();
1451 if (name.length() >= (1U << 30))
1452 throwRuntimeError(
"keylength >= 2^30");
1453 if (features_.rejectDupKeys_ && currentValue().isMember(name)) {
1454 String msg =
"Duplicate key: '" + name +
"'";
1455 return addErrorAndRecover(msg, tokenName, tokenObjectEnd);
1459 if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
1460 return addErrorAndRecover(
"Missing ':' after object member name", colon,
1463 Value& value = currentValue()[name];
1464 nodes_.push(&value);
1465 bool ok = readValue();
1468 return recoverFromError(tokenObjectEnd);
1471 if (!readTokenSkippingComments(comma) ||
1472 (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator)) {
1473 return addErrorAndRecover(
"Missing ',' or '}' in object declaration",
1474 comma, tokenObjectEnd);
1476 if (comma.type_ == tokenObjectEnd)
1479 return addErrorAndRecover(
"Missing '}' or object member name", tokenName,
1483bool OurReader::readArray(Token& token) {
1485 currentValue().swapPayload(init);
1486 currentValue().setOffsetStart(token.start_ - begin_);
1490 if (current_ != end_ && *current_ ==
']' &&
1492 (features_.allowTrailingCommas_ &&
1493 !features_.allowDroppedNullPlaceholders_)))
1497 readToken(endArray);
1500 Value& value = currentValue()[index++];
1501 nodes_.push(&value);
1502 bool ok = readValue();
1505 return recoverFromError(tokenArrayEnd);
1509 ok = readTokenSkippingComments(currentToken);
1510 bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
1511 currentToken.type_ != tokenArrayEnd);
1512 if (!ok || badTokenType) {
1513 return addErrorAndRecover(
"Missing ',' or ']' in array declaration",
1514 currentToken, tokenArrayEnd);
1516 if (currentToken.type_ == tokenArrayEnd)
1522bool OurReader::decodeNumber(Token& token) {
1524 if (!decodeNumber(token, decoded))
1526 currentValue().swapPayload(decoded);
1527 currentValue().setOffsetStart(token.start_ - begin_);
1528 currentValue().setOffsetLimit(token.end_ - begin_);
1532bool OurReader::decodeNumber(Token& token, Value& decoded) {
1536 Location current = token.start_;
1537 const bool isNegative = *current ==
'-';
1546 "Int must be smaller than UInt");
1553 "The absolute value of minLargestInt must be greater than or "
1554 "equal to maxLargestInt");
1556 "The absolute value of minLargestInt must be only 1 magnitude "
1557 "larger than maxLargest Int");
1568 static constexpr auto negative_threshold =
1570 static constexpr auto negative_last_digit =
1574 isNegative ? negative_threshold : positive_threshold;
1576 isNegative ? negative_last_digit : positive_last_digit;
1579 while (current < token.end_) {
1580 Char c = *current++;
1581 if (c <
'0' || c >
'9')
1582 return decodeDouble(token, decoded);
1584 const auto digit(
static_cast<Value::UInt>(c -
'0'));
1585 if (value >= threshold) {
1591 if (value > threshold || current != token.end_ ||
1592 digit > max_last_digit) {
1593 return decodeDouble(token, decoded);
1596 value = value * 10 + digit;
1601 const auto last_digit =
static_cast<Value::UInt>(value % 10);
1612bool OurReader::decodeDouble(Token& token) {
1614 if (!decodeDouble(token, decoded))
1616 currentValue().swapPayload(decoded);
1617 currentValue().setOffsetStart(token.start_ - begin_);
1618 currentValue().setOffsetLimit(token.end_ - begin_);
1622bool OurReader::decodeDouble(Token& token, Value& decoded) {
1625 if (!(is >> value)) {
1626 if (value == std::numeric_limits<double>::max())
1627 value = std::numeric_limits<double>::infinity();
1628 else if (value == std::numeric_limits<double>::lowest())
1629 value = -std::numeric_limits<double>::infinity();
1630 else if (!std::isinf(value))
1632 "'" +
String(token.start_, token.end_) +
"' is not a number.", token);
1638bool OurReader::decodeString(Token& token) {
1640 if (!decodeString(token, decoded_string))
1642 Value decoded(decoded_string);
1643 currentValue().swapPayload(decoded);
1644 currentValue().setOffsetStart(token.start_ - begin_);
1645 currentValue().setOffsetLimit(token.end_ - begin_);
1649bool OurReader::decodeString(Token& token,
String& decoded) {
1650 decoded.reserve(
static_cast<size_t>(token.end_ - token.start_ - 2));
1651 Location current = token.start_ + 1;
1652 Location end = token.end_ - 1;
1653 while (current != end) {
1654 Char c = *current++;
1659 return addError(
"Empty escape sequence in string", token, current);
1660 Char escape = *current++;
1687 unsigned int unicode;
1688 if (!decodeUnicodeCodePoint(token, current, end, unicode))
1693 return addError(
"Bad escape sequence in string", token, current);
1702bool OurReader::decodeUnicodeCodePoint(Token& token, Location& current,
1703 Location end,
unsigned int& unicode) {
1705 if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
1707 if (unicode >= 0xD800 && unicode <= 0xDBFF) {
1709 if (end - current < 6)
1711 "additional six characters expected to parse unicode surrogate pair.",
1713 if (*(current++) ==
'\\' && *(current++) ==
'u') {
1714 unsigned int surrogatePair;
1715 if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
1716 unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
1720 return addError(
"expecting another \\u token to begin the second half of "
1721 "a unicode surrogate pair",
1727bool OurReader::decodeUnicodeEscapeSequence(Token& token, Location& current,
1729 unsigned int& ret_unicode) {
1730 if (end - current < 4)
1732 "Bad unicode escape sequence in string: four digits expected.", token,
1735 for (
int index = 0; index < 4; ++index) {
1736 Char c = *current++;
1738 if (c >=
'0' && c <=
'9')
1740 else if (c >=
'a' && c <=
'f')
1741 unicode += c -
'a' + 10;
1742 else if (c >=
'A' && c <=
'F')
1743 unicode += c -
'A' + 10;
1746 "Bad unicode escape sequence in string: hexadecimal digit expected.",
1749 ret_unicode =
static_cast<unsigned int>(unicode);
1753bool OurReader::addError(
const String& message, Token& token, Location extra) {
1755 info.token_ = token;
1756 info.message_ = message;
1757 info.extra_ = extra;
1758 errors_.push_back(info);
1762bool OurReader::recoverFromError(TokenType skipUntilToken) {
1763 size_t errorCount = errors_.size();
1766 if (!readToken(skip))
1767 errors_.resize(errorCount);
1768 if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
1771 errors_.resize(errorCount);
1775bool OurReader::addErrorAndRecover(
const String& message, Token& token,
1776 TokenType skipUntilToken) {
1777 addError(message, token);
1778 return recoverFromError(skipUntilToken);
1781Value& OurReader::currentValue() {
return *(nodes_.top()); }
1783OurReader::Char OurReader::getNextChar() {
1784 if (current_ == end_)
1789void OurReader::getLocationLineAndColumn(Location location,
int& line,
1790 int& column)
const {
1791 Location current = begin_;
1792 Location lastLineStart = current;
1794 while (current < location && current != end_) {
1795 Char c = *current++;
1797 if (current != end_ && *current ==
'\n')
1799 lastLineStart = current;
1801 }
else if (c ==
'\n') {
1802 lastLineStart = current;
1807 column = int(location - lastLineStart) + 1;
1811String OurReader::getLocationLineAndColumn(Location location)
const {
1813 getLocationLineAndColumn(location, line, column);
1814 char buffer[18 + 16 + 16 + 1];
1815 jsoncpp_snprintf(buffer,
sizeof(buffer),
"Line %d, Column %d", line, column);
1819String OurReader::getFormattedErrorMessages()
const {
1821 for (
const auto& error : errors_) {
1823 "* " + getLocationLineAndColumn(error.token_.start_) +
"\n";
1824 formattedMessage +=
" " + error.message_ +
"\n";
1827 "See " + getLocationLineAndColumn(error.extra_) +
" for detail.\n";
1829 return formattedMessage;
1832std::vector<CharReader::StructuredError>
1833OurReader::getStructuredErrors()
const {
1834 std::vector<CharReader::StructuredError> allErrors;
1835 for (
const auto& error : errors_) {
1836 CharReader::StructuredError structured;
1837 structured.offset_start = error.token_.start_ - begin_;
1838 structured.offset_limit = error.token_.end_ - begin_;
1839 structured.message = error.message_;
1840 allErrors.push_back(structured);
1845class OurCharReader :
public CharReader {
1848 OurCharReader(
bool collectComments, OurFeatures
const& features)
1850 std::unique_ptr<OurImpl>(new OurImpl(collectComments, features))) {}
1853 class OurImpl :
public Impl {
1855 OurImpl(
bool collectComments, OurFeatures
const& features)
1856 : collectComments_(collectComments), reader_(features) {}
1858 bool parse(
char const* beginDoc,
char const* endDoc, Value* root,
1860 bool ok = reader_.parse(beginDoc, endDoc, *root, collectComments_);
1862 *errs = reader_.getFormattedErrorMessages();
1867 std::vector<CharReader::StructuredError>
1868 getStructuredErrors()
const override {
1869 return reader_.getStructuredErrors();
1873 bool const collectComments_;
1882 OurFeatures features = OurFeatures::all();
1884 features.allowTrailingCommas_ =
settings_[
"allowTrailingCommas"].
asBool();
1886 features.allowDroppedNullPlaceholders_ =
1889 features.allowSingleQuotes_ =
settings_[
"allowSingleQuotes"].
asBool();
1893 features.stackLimit_ =
static_cast<size_t>(
settings_[
"stackLimit"].
asUInt());
1896 features.allowSpecialFloats_ =
settings_[
"allowSpecialFloats"].
asBool();
1898 return new OurCharReader(collectComments, features);
1902 static const auto& valid_keys = *
new std::set<String>{
1905 "allowTrailingCommas",
1907 "allowDroppedNullPlaceholders",
1909 "allowSingleQuotes",
1913 "allowSpecialFloats",
1917 auto key = si.name();
1918 if (valid_keys.count(key))
1921 (*invalid)[key] = *si;
1925 return invalid ? invalid->
empty() :
true;
1934 (*settings)[
"allowComments"] =
false;
1935 (*settings)[
"allowTrailingCommas"] =
false;
1936 (*settings)[
"strictRoot"] =
true;
1937 (*settings)[
"allowDroppedNullPlaceholders"] =
false;
1938 (*settings)[
"allowNumericKeys"] =
false;
1939 (*settings)[
"allowSingleQuotes"] =
false;
1940 (*settings)[
"stackLimit"] = 1000;
1941 (*settings)[
"failIfExtra"] =
true;
1942 (*settings)[
"rejectDupKeys"] =
true;
1943 (*settings)[
"allowSpecialFloats"] =
false;
1944 (*settings)[
"skipBom"] =
true;
1950 (*settings)[
"collectComments"] =
true;
1951 (*settings)[
"allowComments"] =
true;
1952 (*settings)[
"allowTrailingCommas"] =
true;
1953 (*settings)[
"strictRoot"] =
false;
1954 (*settings)[
"allowDroppedNullPlaceholders"] =
false;
1955 (*settings)[
"allowNumericKeys"] =
false;
1956 (*settings)[
"allowSingleQuotes"] =
false;
1957 (*settings)[
"stackLimit"] = 1000;
1958 (*settings)[
"failIfExtra"] =
false;
1959 (*settings)[
"rejectDupKeys"] =
false;
1960 (*settings)[
"allowSpecialFloats"] =
false;
1961 (*settings)[
"skipBom"] =
true;
1967 (*settings)[
"allowComments"] =
false;
1968 (*settings)[
"allowTrailingCommas"] =
false;
1969 (*settings)[
"strictRoot"] =
false;
1970 (*settings)[
"allowDroppedNullPlaceholders"] =
false;
1971 (*settings)[
"allowNumericKeys"] =
false;
1972 (*settings)[
"allowSingleQuotes"] =
false;
1973 (*settings)[
"stackLimit"] = 1000;
1974 (*settings)[
"failIfExtra"] =
true;
1975 (*settings)[
"rejectDupKeys"] =
false;
1976 (*settings)[
"allowSpecialFloats"] =
false;
1977 (*settings)[
"skipBom"] =
false;
1981std::vector<CharReader::StructuredError>
1983 return _impl->getStructuredErrors();
1988 return _impl->parse(beginDoc, endDoc, root, errs);
1997 ssin << sin.rdbuf();
1998 String doc = std::move(ssin).str();
1999 char const* begin = doc.data();
2000 char const* end = begin + doc.size();
2003 return reader->parse(begin, end, root, errs);
2011 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_.
static void ecma404Mode(Json::Value *settings)
ECMA-404 mode.
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.
CharReader(std::unique_ptr< Impl > impl)
std::vector< StructuredError > getStructuredErrors() const
Returns a vector of structured errors encountered while parsing.
virtual bool parse(char const *beginDoc, char const *endDoc, Value *root, String *errs)
Read a Value from a JSON document.
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.