7 #if !defined(JSON_IS_AMALGAMATION)
12 #endif // if !defined(JSON_IS_AMALGAMATION)
29 #if !defined(_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES)
30 #define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
31 #endif //_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES
36 #pragma warning(disable : 4996)
41 #if !defined(JSONCPP_DEPRECATED_STACK_LIMIT)
42 #define JSONCPP_DEPRECATED_STACK_LIMIT 256
72 return std::any_of(begin, end, [](
char b) {
return b ==
'\n' || b ==
'\r'; });
83 bool collectComments) {
84 document_.assign(document.begin(), document.end());
85 const char* begin = document_.c_str();
86 const char* end = begin + document_.length();
87 return parse(begin, end, root, collectComments);
91 document_.assign(std::istreambuf_iterator<char>(is),
92 std::istreambuf_iterator<char>());
93 return parse(document_.data(), document_.data() + document_.size(), root,
98 bool collectComments) {
100 collectComments =
false;
105 collectComments_ = collectComments;
107 lastValueEnd_ =
nullptr;
108 lastValue_ =
nullptr;
109 commentsBefore_.clear();
111 while (!nodes_.empty())
115 bool successful = readValue();
117 readTokenSkippingComments(token);
118 if (collectComments_ && !commentsBefore_.empty())
124 token.type_ = tokenError;
125 token.start_ = beginDoc;
128 "A valid JSON document must be either an array or an object value.",
136 bool Reader::readValue() {
143 throwRuntimeError(
"Exceeded stackLimit in readValue().");
150 readTokenSkippingComments(token);
151 bool successful =
true;
153 if (collectComments_ && !commentsBefore_.empty()) {
155 commentsBefore_.clear();
158 switch (token.type_) {
159 case tokenObjectBegin:
160 successful = readObject(token);
163 case tokenArrayBegin:
164 successful = readArray(token);
168 successful = decodeNumber(token);
171 successful = decodeString(token);
191 case tokenArraySeparator:
207 return addError(
"Syntax error: value, object or array expected.", token);
210 if (collectComments_) {
211 lastValueEnd_ = current_;
212 lastValue_ = ¤tValue();
218 bool Reader::readTokenSkippingComments(Token& token) {
219 bool success = readToken(token);
221 while (success && token.type_ == tokenComment) {
222 success = readToken(token);
228 bool Reader::readToken(Token& token) {
230 token.start_ = current_;
231 Char c = getNextChar();
235 token.type_ = tokenObjectBegin;
238 token.type_ = tokenObjectEnd;
241 token.type_ = tokenArrayBegin;
244 token.type_ = tokenArrayEnd;
247 token.type_ = tokenString;
251 token.type_ = tokenComment;
265 token.type_ = tokenNumber;
269 token.type_ = tokenTrue;
270 ok = match(
"rue", 3);
273 token.type_ = tokenFalse;
274 ok = match(
"alse", 4);
277 token.type_ = tokenNull;
278 ok = match(
"ull", 3);
281 token.type_ = tokenArraySeparator;
284 token.type_ = tokenMemberSeparator;
287 token.type_ = tokenEndOfStream;
294 token.type_ = tokenError;
295 token.end_ = current_;
299 void Reader::skipSpaces() {
300 while (current_ != end_) {
302 if (c ==
' ' || c ==
'\t' || c ==
'\r' || c ==
'\n')
309 bool Reader::match(
const Char* pattern,
int patternLength) {
310 if (end_ - current_ < patternLength)
312 int index = patternLength;
314 if (current_[index] != pattern[index])
316 current_ += patternLength;
320 bool Reader::readComment() {
321 Location commentBegin = current_ - 1;
322 Char c = getNextChar();
323 bool successful =
false;
325 successful = readCStyleComment();
327 successful = readCppStyleComment();
331 if (collectComments_) {
333 if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
334 if (c !=
'*' || !containsNewLine(commentBegin, current_))
338 addComment(commentBegin, current_, placement);
345 normalized.reserve(static_cast<size_t>(end - begin));
347 while (current != end) {
350 if (current != end && *current ==
'\n')
362 void Reader::addComment(Location begin, Location end,
364 assert(collectComments_);
365 const String& normalized = normalizeEOL(begin, end);
367 assert(lastValue_ !=
nullptr);
368 lastValue_->
setComment(normalized, placement);
370 commentsBefore_ += normalized;
374 bool Reader::readCStyleComment() {
375 while ((current_ + 1) < end_) {
376 Char c = getNextChar();
377 if (c ==
'*' && *current_ ==
'/')
380 return getNextChar() ==
'/';
383 bool Reader::readCppStyleComment() {
384 while (current_ != end_) {
385 Char c = getNextChar();
390 if (current_ != end_ && *current_ ==
'\n')
399 void Reader::readNumber() {
403 while (c >=
'0' && c <=
'9')
404 c = (current_ = p) < end_ ? *p++ :
'\0';
407 c = (current_ = p) < end_ ? *p++ :
'\0';
408 while (c >=
'0' && c <=
'9')
409 c = (current_ = p) < end_ ? *p++ :
'\0';
412 if (c ==
'e' || c ==
'E') {
413 c = (current_ = p) < end_ ? *p++ :
'\0';
414 if (c ==
'+' || c ==
'-')
415 c = (current_ = p) < end_ ? *p++ :
'\0';
416 while (c >=
'0' && c <=
'9')
417 c = (current_ = p) < end_ ? *p++ :
'\0';
421 bool Reader::readString() {
423 while (current_ != end_) {
433 bool Reader::readObject(Token& token) {
439 while (readTokenSkippingComments(tokenName)) {
440 if (tokenName.type_ == tokenObjectEnd && name.empty())
443 if (tokenName.type_ == tokenString) {
444 if (!decodeString(tokenName, name))
445 return recoverFromError(tokenObjectEnd);
448 if (!decodeNumber(tokenName, numberName))
449 return recoverFromError(tokenObjectEnd);
450 name = numberName.asString();
456 if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
457 return addErrorAndRecover(
"Missing ':' after object member name", colon,
460 Value& value = currentValue()[name];
462 bool ok = readValue();
465 return recoverFromError(tokenObjectEnd);
468 if (!readTokenSkippingComments(comma) ||
469 (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator)) {
470 return addErrorAndRecover(
"Missing ',' or '}' in object declaration",
471 comma, tokenObjectEnd);
473 if (comma.type_ == tokenObjectEnd)
476 return addErrorAndRecover(
"Missing '}' or object member name", tokenName,
480 bool Reader::readArray(Token& token) {
485 if (current_ != end_ && *current_ ==
']')
493 Value& value = currentValue()[index++];
495 bool ok = readValue();
498 return recoverFromError(tokenArrayEnd);
502 ok = readTokenSkippingComments(currentToken);
503 bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
504 currentToken.type_ != tokenArrayEnd);
505 if (!ok || badTokenType) {
506 return addErrorAndRecover(
"Missing ',' or ']' in array declaration",
507 currentToken, tokenArrayEnd);
509 if (currentToken.type_ == tokenArrayEnd)
515 bool Reader::decodeNumber(Token& token) {
517 if (!decodeNumber(token, decoded))
525 bool Reader::decodeNumber(Token& token, Value& decoded) {
530 bool isNegative = *current ==
'-';
540 while (current < token.end_) {
542 if (c < '0' || c >
'9')
543 return decodeDouble(token, decoded);
544 auto digit(static_cast<Value::UInt>(c -
'0'));
545 if (value >= threshold) {
550 if (value > threshold || current != token.end_ ||
551 digit > maxIntegerValue % 10) {
552 return decodeDouble(token, decoded);
555 value = value * 10 + digit;
557 if (isNegative && value == maxIntegerValue)
568 bool Reader::decodeDouble(Token& token) {
570 if (!decodeDouble(token, decoded))
578 bool Reader::decodeDouble(Token& token, Value& decoded) {
581 is.imbue(std::locale::classic());
582 if (!(is >> value)) {
583 if (value == std::numeric_limits<double>::max())
584 value = std::numeric_limits<double>::infinity();
585 else if (value == std::numeric_limits<double>::lowest())
586 value = -std::numeric_limits<double>::infinity();
587 else if (!std::isinf(value))
589 "'" +
String(token.start_, token.end_) +
"' is not a number.", token);
595 bool Reader::decodeString(Token& token) {
597 if (!decodeString(token, decoded_string))
599 Value decoded(decoded_string);
606 bool Reader::decodeString(Token& token,
String& decoded) {
607 decoded.reserve(static_cast<size_t>(token.end_ - token.start_ - 2));
608 Location current = token.start_ + 1;
610 while (current != end) {
616 return addError(
"Empty escape sequence in string", token, current);
617 Char escape = *current++;
644 unsigned int unicode;
645 if (!decodeUnicodeCodePoint(token, current, end, unicode))
650 return addError(
"Bad escape sequence in string", token, current);
653 if (static_cast<unsigned char>(c) < 0x20)
654 return addError(
"Control character in string", token, current - 1);
661 bool Reader::decodeUnicodeCodePoint(Token& token, Location& current,
662 Location end,
unsigned int& unicode) {
664 if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
666 if (unicode >= 0xD800 && unicode <= 0xDBFF) {
668 if (end - current < 6)
670 "additional six characters expected to parse unicode surrogate pair.",
672 if (*(current++) ==
'\\' && *(current++) ==
'u') {
673 unsigned int surrogatePair;
674 if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
675 unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
679 return addError(
"expecting another \\u token to begin the second half of "
680 "a unicode surrogate pair",
686 bool Reader::decodeUnicodeEscapeSequence(Token& token, Location& current,
688 unsigned int& ret_unicode) {
689 if (end - current < 4)
691 "Bad unicode escape sequence in string: four digits expected.", token,
694 for (
int index = 0; index < 4; ++index) {
697 if (c >=
'0' && c <=
'9')
699 else if (c >=
'a' && c <=
'f')
700 unicode += c -
'a' + 10;
701 else if (c >=
'A' && c <=
'F')
702 unicode += c -
'A' + 10;
705 "Bad unicode escape sequence in string: hexadecimal digit expected.",
708 ret_unicode =
static_cast<unsigned int>(unicode);
712 bool Reader::addError(
const String& message, Token& token, Location extra) {
715 info.message_ = message;
717 errors_.push_back(info);
721 bool Reader::recoverFromError(TokenType skipUntilToken) {
722 size_t const errorCount = errors_.size();
725 if (!readToken(skip))
726 errors_.resize(errorCount);
727 if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
730 errors_.resize(errorCount);
734 bool Reader::addErrorAndRecover(
const String& message, Token& token,
735 TokenType skipUntilToken) {
736 addError(message, token);
737 return recoverFromError(skipUntilToken);
740 Value& Reader::currentValue() {
return *(nodes_.top()); }
743 if (current_ == end_)
748 void Reader::getLocationLineAndColumn(Location location,
int& line,
753 while (current < location && current != end_) {
756 if (current != end_ && *current ==
'\n')
758 lastLineStart = current;
760 }
else if (c ==
'\n') {
761 lastLineStart = current;
766 column = int(location - lastLineStart) + 1;
770 String Reader::getLocationLineAndColumn(Location location)
const {
772 getLocationLineAndColumn(location, line, column);
773 char buffer[18 + 16 + 16 + 1];
774 jsoncpp_snprintf(buffer,
sizeof(buffer),
"Line %d, Column %d", line, column);
779 String Reader::getFormatedErrorMessages()
const {
785 for (
const auto& error : errors_) {
787 "* " + getLocationLineAndColumn(error.token_.start_) +
"\n";
788 formattedMessage +=
" " + error.message_ +
"\n";
791 "See " + getLocationLineAndColumn(error.extra_) +
" for detail.\n";
793 return formattedMessage;
797 std::vector<Reader::StructuredError> allErrors;
798 for (
const auto& error : errors_) {
802 structured.
message = error.message_;
803 allErrors.push_back(structured);
809 ptrdiff_t
const length = end_ - begin_;
813 token.type_ = tokenError;
818 info.message_ = message;
819 info.extra_ =
nullptr;
820 errors_.push_back(info);
825 const Value& extra) {
826 ptrdiff_t
const length = end_ - begin_;
831 token.type_ = tokenError;
836 info.message_ = message;
838 errors_.push_back(info);
848 static OurFeatures all();
850 bool allowTrailingCommas_;
852 bool allowDroppedNullPlaceholders_;
853 bool allowNumericKeys_;
854 bool allowSingleQuotes_;
857 bool allowSpecialFloats_;
862 OurFeatures OurFeatures::all() {
return {}; }
872 using Location =
const Char*;
874 explicit OurReader(OurFeatures
const& features);
875 bool parse(
const char* beginDoc,
const char* endDoc, Value& root,
876 bool collectComments =
true);
877 String getFormattedErrorMessages()
const;
878 std::vector<CharReader::StructuredError> getStructuredErrors()
const;
881 OurReader(OurReader
const&);
882 void operator=(OurReader
const&);
885 tokenEndOfStream = 0,
899 tokenMemberSeparator,
918 using Errors = std::deque<ErrorInfo>;
920 bool readToken(Token& token);
921 bool readTokenSkippingComments(Token& token);
923 void skipBom(
bool skipBom);
924 bool match(
const Char* pattern,
int patternLength);
926 bool readCStyleComment(
bool* containsNewLineResult);
927 bool readCppStyleComment();
929 bool readStringSingleQuote();
930 bool readNumber(
bool checkInf);
932 bool readObject(Token& token);
933 bool readArray(Token& token);
934 bool decodeNumber(Token& token);
935 bool decodeNumber(Token& token, Value& decoded);
936 bool decodeString(Token& token);
937 bool decodeString(Token& token,
String& decoded);
938 bool decodeDouble(Token& token);
939 bool decodeDouble(Token& token, Value& decoded);
940 bool decodeUnicodeCodePoint(Token& token, Location& current, Location end,
941 unsigned int& unicode);
942 bool decodeUnicodeEscapeSequence(Token& token, Location& current,
943 Location end,
unsigned int& unicode);
944 bool addError(
const String& message, Token& token, Location extra =
nullptr);
945 bool recoverFromError(TokenType skipUntilToken);
946 bool addErrorAndRecover(
const String& message, Token& token,
947 TokenType skipUntilToken);
948 void skipUntilSpace();
949 Value& currentValue();
951 void getLocationLineAndColumn(Location location,
int& line,
953 String getLocationLineAndColumn(Location location)
const;
956 static String normalizeEOL(Location begin, Location end);
957 static bool containsNewLine(Location begin, Location end);
959 using Nodes = std::stack<Value*>;
964 Location begin_ =
nullptr;
965 Location end_ =
nullptr;
966 Location current_ =
nullptr;
967 Location lastValueEnd_ =
nullptr;
968 Value* lastValue_ =
nullptr;
969 bool lastValueHasAComment_ =
false;
972 OurFeatures
const features_;
973 bool collectComments_ =
false;
985 static thread_local
size_t count = 0;
989 bool OurReader::containsNewLine(OurReader::Location begin,
990 OurReader::Location end) {
992 return std::any_of(begin, end, [](
char b) {
return b ==
'\n' || b ==
'\r'; });
995 OurReader::OurReader(OurFeatures
const& features) : features_(features) {}
997 bool OurReader::parse(
const char* beginDoc,
const char* endDoc, Value& root,
998 bool collectComments) {
999 if (!features_.allowComments_) {
1000 collectComments =
false;
1005 collectComments_ = collectComments;
1007 lastValueEnd_ =
nullptr;
1008 lastValue_ =
nullptr;
1009 commentsBefore_.clear();
1011 while (!nodes_.empty())
1016 skipBom(features_.skipBom_);
1017 bool successful = readValue();
1020 readTokenSkippingComments(token);
1021 if (features_.failIfExtra_ && (token.type_ != tokenEndOfStream)) {
1022 addError(
"Extra non-whitespace after JSON value.", token);
1025 if (collectComments_ && !commentsBefore_.empty())
1027 if (features_.strictRoot_) {
1028 if (!root.isArray() && !root.isObject()) {
1031 token.type_ = tokenError;
1032 token.start_ = beginDoc;
1033 token.end_ = endDoc;
1035 "A valid JSON document must be either an array or an object value.",
1043 bool OurReader::readValue() {
1045 if (nodes_.size() > features_.stackLimit_)
1046 throwRuntimeError(
"Exceeded stackLimit in readValue().");
1048 readTokenSkippingComments(token);
1049 bool successful =
true;
1051 if (collectComments_ && !commentsBefore_.empty()) {
1053 commentsBefore_.clear();
1056 switch (token.type_) {
1057 case tokenObjectBegin:
1058 successful = readObject(token);
1059 currentValue().setOffsetLimit(current_ - begin_);
1061 case tokenArrayBegin:
1062 successful = readArray(token);
1063 currentValue().setOffsetLimit(current_ - begin_);
1066 successful = decodeNumber(token);
1069 successful = decodeString(token);
1073 currentValue().swapPayload(v);
1074 currentValue().setOffsetStart(token.start_ - begin_);
1075 currentValue().setOffsetLimit(token.end_ - begin_);
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_);
1090 Value v(std::numeric_limits<double>::quiet_NaN());
1091 currentValue().swapPayload(v);
1092 currentValue().setOffsetStart(token.start_ - begin_);
1093 currentValue().setOffsetLimit(token.end_ - begin_);
1096 Value v(std::numeric_limits<double>::infinity());
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_);
1107 case tokenArraySeparator:
1108 case tokenObjectEnd:
1110 if (features_.allowDroppedNullPlaceholders_) {
1115 currentValue().swapPayload(v);
1116 currentValue().setOffsetStart(current_ - begin_ - 1);
1117 currentValue().setOffsetLimit(current_ - begin_);
1121 currentValue().setOffsetStart(token.start_ - begin_);
1122 currentValue().setOffsetLimit(token.end_ - begin_);
1123 return addError(
"Syntax error: value, object or array expected.", token);
1126 if (collectComments_) {
1127 lastValueEnd_ = current_;
1128 lastValueHasAComment_ =
false;
1129 lastValue_ = ¤tValue();
1135 bool OurReader::readTokenSkippingComments(Token& token) {
1136 bool success = readToken(token);
1137 if (features_.allowComments_) {
1138 while (success && token.type_ == tokenComment) {
1139 success = readToken(token);
1145 bool OurReader::readToken(Token& token) {
1147 token.start_ = current_;
1148 Char c = getNextChar();
1152 token.type_ = tokenObjectBegin;
1155 token.type_ = tokenObjectEnd;
1158 token.type_ = tokenArrayBegin;
1161 token.type_ = tokenArrayEnd;
1164 token.type_ = tokenString;
1168 if (features_.allowSingleQuotes_) {
1169 token.type_ = tokenString;
1170 ok = readStringSingleQuote();
1177 token.type_ = tokenComment;
1190 token.type_ = tokenNumber;
1194 if (readNumber(
true)) {
1195 token.type_ = tokenNumber;
1197 token.type_ = tokenNegInf;
1198 ok = features_.allowSpecialFloats_ && match(
"nfinity", 7);
1202 if (readNumber(
true)) {
1203 token.type_ = tokenNumber;
1205 token.type_ = tokenPosInf;
1206 ok = features_.allowSpecialFloats_ && match(
"nfinity", 7);
1210 token.type_ = tokenTrue;
1211 ok = match(
"rue", 3);
1214 token.type_ = tokenFalse;
1215 ok = match(
"alse", 4);
1218 token.type_ = tokenNull;
1219 ok = match(
"ull", 3);
1222 if (features_.allowSpecialFloats_) {
1223 token.type_ = tokenNaN;
1224 ok = match(
"aN", 2);
1230 if (features_.allowSpecialFloats_) {
1231 token.type_ = tokenPosInf;
1232 ok = match(
"nfinity", 7);
1238 token.type_ = tokenArraySeparator;
1241 token.type_ = tokenMemberSeparator;
1244 token.type_ = tokenEndOfStream;
1251 token.type_ = tokenError;
1252 token.end_ = current_;
1256 void OurReader::skipSpaces() {
1257 while (current_ != end_) {
1259 if (c ==
' ' || c ==
'\t' || c ==
'\r' || c ==
'\n')
1266 void OurReader::skipBom(
bool skipBom) {
1269 if ((end_ - begin_) >= 3 && strncmp(begin_,
"\xEF\xBB\xBF", 3) == 0) {
1276 bool OurReader::match(
const Char* pattern,
int patternLength) {
1277 if (end_ - current_ < patternLength)
1279 int index = patternLength;
1281 if (current_[index] != pattern[index])
1283 current_ += patternLength;
1287 bool OurReader::readComment() {
1288 const Location commentBegin = current_ - 1;
1289 const Char c = getNextChar();
1290 bool successful =
false;
1291 bool cStyleWithEmbeddedNewline =
false;
1293 const bool isCStyleComment = (c ==
'*');
1294 const bool isCppStyleComment = (c ==
'/');
1295 if (isCStyleComment) {
1296 successful = readCStyleComment(&cStyleWithEmbeddedNewline);
1297 }
else if (isCppStyleComment) {
1298 successful = readCppStyleComment();
1304 if (collectComments_) {
1307 if (!lastValueHasAComment_) {
1308 if (lastValueEnd_ && !containsNewLine(lastValueEnd_, commentBegin)) {
1309 if (isCppStyleComment || !cStyleWithEmbeddedNewline) {
1317 lastValueHasAComment_ =
true;
1320 addComment(commentBegin, current_, placement);
1325 String OurReader::normalizeEOL(OurReader::Location begin,
1326 OurReader::Location end) {
1328 normalized.reserve(static_cast<size_t>(end - begin));
1329 OurReader::Location current = begin;
1330 while (current != end) {
1331 char c = *current++;
1333 if (current != end && *current ==
'\n')
1345 void OurReader::addComment(Location begin, Location end,
1347 assert(collectComments_);
1348 const String& normalized = normalizeEOL(begin, end);
1350 assert(lastValue_ !=
nullptr);
1351 lastValue_->setComment(normalized, placement);
1353 commentsBefore_ += normalized;
1357 bool OurReader::readCStyleComment(
bool* containsNewLineResult) {
1358 *containsNewLineResult =
false;
1360 while ((current_ + 1) < end_) {
1361 Char c = getNextChar();
1362 if (c ==
'*' && *current_ ==
'/')
1365 *containsNewLineResult =
true;
1368 return getNextChar() ==
'/';
1371 bool OurReader::readCppStyleComment() {
1372 while (current_ != end_) {
1373 Char c = getNextChar();
1378 if (current_ != end_ && *current_ ==
'\n')
1387 bool OurReader::readNumber(
bool checkInf) {
1388 Location p = current_;
1389 if (checkInf && p != end_ && *p ==
'I') {
1395 while (c >=
'0' && c <=
'9')
1396 c = (current_ = p) < end_ ? *p++ :
'\0';
1399 c = (current_ = p) < end_ ? *p++ :
'\0';
1400 while (c >=
'0' && c <=
'9')
1401 c = (current_ = p) < end_ ? *p++ :
'\0';
1404 if (c ==
'e' || c ==
'E') {
1405 c = (current_ = p) < end_ ? *p++ :
'\0';
1406 if (c ==
'+' || c ==
'-')
1407 c = (current_ = p) < end_ ? *p++ :
'\0';
1408 while (c >=
'0' && c <=
'9')
1409 c = (current_ = p) < end_ ? *p++ :
'\0';
1413 bool OurReader::readString() {
1415 while (current_ != end_) {
1425 bool OurReader::readStringSingleQuote() {
1427 while (current_ != end_) {
1437 bool OurReader::readObject(Token& token) {
1441 currentValue().swapPayload(init);
1442 currentValue().setOffsetStart(token.start_ - begin_);
1443 while (readTokenSkippingComments(tokenName)) {
1444 if (tokenName.type_ == tokenObjectEnd &&
1446 features_.allowTrailingCommas_))
1449 if (tokenName.type_ == tokenString) {
1450 if (!decodeString(tokenName, name))
1451 return recoverFromError(tokenObjectEnd);
1452 }
else if (tokenName.type_ == tokenNumber && features_.allowNumericKeys_) {
1454 if (!decodeNumber(tokenName, numberName))
1455 return recoverFromError(tokenObjectEnd);
1456 name = numberName.asString();
1460 if (name.length() >= (1U << 30))
1461 throwRuntimeError(
"keylength >= 2^30");
1462 if (features_.rejectDupKeys_ && currentValue().isMember(name)) {
1463 String msg =
"Duplicate key: '" + name +
"'";
1464 return addErrorAndRecover(msg, tokenName, tokenObjectEnd);
1468 if (!readToken(colon) || colon.type_ != tokenMemberSeparator) {
1469 return addErrorAndRecover(
"Missing ':' after object member name", colon,
1472 Value& value = currentValue()[name];
1473 nodes_.push(&value);
1474 bool ok = readValue();
1477 return recoverFromError(tokenObjectEnd);
1480 if (!readTokenSkippingComments(comma) ||
1481 (comma.type_ != tokenObjectEnd && comma.type_ != tokenArraySeparator)) {
1482 return addErrorAndRecover(
"Missing ',' or '}' in object declaration",
1483 comma, tokenObjectEnd);
1485 if (comma.type_ == tokenObjectEnd)
1488 return addErrorAndRecover(
"Missing '}' or object member name", tokenName,
1492 bool OurReader::readArray(Token& token) {
1494 currentValue().swapPayload(init);
1495 currentValue().setOffsetStart(token.start_ - begin_);
1499 if (current_ != end_ && *current_ ==
']' &&
1501 (features_.allowTrailingCommas_ &&
1502 !features_.allowDroppedNullPlaceholders_)))
1506 readToken(endArray);
1509 Value& value = currentValue()[index++];
1510 nodes_.push(&value);
1511 bool ok = readValue();
1514 return recoverFromError(tokenArrayEnd);
1518 ok = readTokenSkippingComments(currentToken);
1519 bool badTokenType = (currentToken.type_ != tokenArraySeparator &&
1520 currentToken.type_ != tokenArrayEnd);
1521 if (!ok || badTokenType) {
1522 return addErrorAndRecover(
"Missing ',' or ']' in array declaration",
1523 currentToken, tokenArrayEnd);
1525 if (currentToken.type_ == tokenArrayEnd)
1531 bool OurReader::decodeNumber(Token& token) {
1533 if (!decodeNumber(token, decoded))
1535 currentValue().swapPayload(decoded);
1536 currentValue().setOffsetStart(token.start_ - begin_);
1537 currentValue().setOffsetLimit(token.end_ - begin_);
1541 bool OurReader::decodeNumber(Token& token, Value& decoded) {
1545 Location current = token.start_;
1546 const bool isNegative = *current ==
'-';
1554 static_assert(Value::maxLargestInt <= Value::maxLargestUInt,
1555 "Int must be smaller than UInt");
1561 static_assert(Value::minLargestInt <= -Value::maxLargestInt,
1562 "The absolute value of minLargestInt must be greater than or "
1563 "equal to maxLargestInt");
1564 static_assert(Value::minLargestInt / 10 >= -Value::maxLargestInt,
1565 "The absolute value of minLargestInt must be only 1 magnitude "
1566 "larger than maxLargest Int");
1569 Value::maxLargestUInt / 10;
1570 static constexpr
Value::UInt positive_last_digit = Value::maxLargestUInt % 10;
1577 static constexpr
auto negative_threshold =
1579 static constexpr
auto negative_last_digit =
1583 isNegative ? negative_threshold : positive_threshold;
1585 isNegative ? negative_last_digit : positive_last_digit;
1588 while (current < token.end_) {
1589 Char c = *current++;
1590 if (c < '0' || c >
'9')
1591 return decodeDouble(token, decoded);
1593 const auto digit(static_cast<Value::UInt>(c -
'0'));
1594 if (value >= threshold) {
1600 if (value > threshold || current != token.end_ ||
1601 digit > max_last_digit) {
1602 return decodeDouble(token, decoded);
1605 value = value * 10 + digit;
1610 const auto last_digit =
static_cast<Value::UInt>(value % 10);
1621 bool OurReader::decodeDouble(Token& token) {
1623 if (!decodeDouble(token, decoded))
1625 currentValue().swapPayload(decoded);
1626 currentValue().setOffsetStart(token.start_ - begin_);
1627 currentValue().setOffsetLimit(token.end_ - begin_);
1631 bool OurReader::decodeDouble(Token& token, Value& decoded) {
1634 is.imbue(std::locale::classic());
1635 if (!(is >> value)) {
1636 if (value == std::numeric_limits<double>::max())
1637 value = std::numeric_limits<double>::infinity();
1638 else if (value == std::numeric_limits<double>::lowest())
1639 value = -std::numeric_limits<double>::infinity();
1640 else if (!std::isinf(value))
1642 "'" +
String(token.start_, token.end_) +
"' is not a number.", token);
1648 bool OurReader::decodeString(Token& token) {
1650 if (!decodeString(token, decoded_string))
1652 Value decoded(decoded_string);
1653 currentValue().swapPayload(decoded);
1654 currentValue().setOffsetStart(token.start_ - begin_);
1655 currentValue().setOffsetLimit(token.end_ - begin_);
1659 bool OurReader::decodeString(Token& token,
String& decoded) {
1660 decoded.reserve(static_cast<size_t>(token.end_ - token.start_ - 2));
1661 Location current = token.start_ + 1;
1662 Location end = token.end_ - 1;
1663 while (current != end) {
1664 Char c = *current++;
1669 return addError(
"Empty escape sequence in string", token, current);
1670 Char escape = *current++;
1697 unsigned int unicode;
1698 if (!decodeUnicodeCodePoint(token, current, end, unicode))
1703 return addError(
"Bad escape sequence in string", token, current);
1706 if (static_cast<unsigned char>(c) < 0x20)
1707 return addError(
"Control character in string", token, current - 1);
1714 bool OurReader::decodeUnicodeCodePoint(Token& token, Location& current,
1715 Location end,
unsigned int& unicode) {
1717 if (!decodeUnicodeEscapeSequence(token, current, end, unicode))
1719 if (unicode >= 0xD800 && unicode <= 0xDBFF) {
1721 if (end - current < 6)
1723 "additional six characters expected to parse unicode surrogate pair.",
1725 if (*(current++) ==
'\\' && *(current++) ==
'u') {
1726 unsigned int surrogatePair;
1727 if (decodeUnicodeEscapeSequence(token, current, end, surrogatePair)) {
1728 unicode = 0x10000 + ((unicode & 0x3FF) << 10) + (surrogatePair & 0x3FF);
1732 return addError(
"expecting another \\u token to begin the second half of "
1733 "a unicode surrogate pair",
1739 bool OurReader::decodeUnicodeEscapeSequence(Token& token, Location& current,
1741 unsigned int& ret_unicode) {
1742 if (end - current < 4)
1744 "Bad unicode escape sequence in string: four digits expected.", token,
1747 for (
int index = 0; index < 4; ++index) {
1748 Char c = *current++;
1750 if (c >=
'0' && c <=
'9')
1752 else if (c >=
'a' && c <=
'f')
1753 unicode += c -
'a' + 10;
1754 else if (c >=
'A' && c <=
'F')
1755 unicode += c -
'A' + 10;
1758 "Bad unicode escape sequence in string: hexadecimal digit expected.",
1761 ret_unicode =
static_cast<unsigned int>(unicode);
1765 bool OurReader::addError(
const String& message, Token& token, Location extra) {
1767 info.token_ = token;
1768 info.message_ = message;
1769 info.extra_ = extra;
1770 errors_.push_back(info);
1774 bool OurReader::recoverFromError(TokenType skipUntilToken) {
1775 size_t errorCount = errors_.size();
1778 if (!readToken(skip))
1779 errors_.resize(errorCount);
1780 if (skip.type_ == skipUntilToken || skip.type_ == tokenEndOfStream)
1783 errors_.resize(errorCount);
1787 bool OurReader::addErrorAndRecover(
const String& message, Token& token,
1788 TokenType skipUntilToken) {
1789 addError(message, token);
1790 return recoverFromError(skipUntilToken);
1793 Value& OurReader::currentValue() {
return *(nodes_.top()); }
1795 OurReader::Char OurReader::getNextChar() {
1796 if (current_ == end_)
1801 void OurReader::getLocationLineAndColumn(Location location,
int& line,
1802 int& column)
const {
1803 Location current = begin_;
1804 Location lastLineStart = current;
1806 while (current < location && current != end_) {
1807 Char c = *current++;
1809 if (current != end_ && *current ==
'\n')
1811 lastLineStart = current;
1813 }
else if (c ==
'\n') {
1814 lastLineStart = current;
1819 column = int(location - lastLineStart) + 1;
1823 String OurReader::getLocationLineAndColumn(Location location)
const {
1825 getLocationLineAndColumn(location, line, column);
1826 char buffer[18 + 16 + 16 + 1];
1827 jsoncpp_snprintf(buffer,
sizeof(buffer),
"Line %d, Column %d", line, column);
1831 String OurReader::getFormattedErrorMessages()
const {
1833 for (
const auto& error : errors_) {
1835 "* " + getLocationLineAndColumn(error.token_.start_) +
"\n";
1836 formattedMessage +=
" " + error.message_ +
"\n";
1839 "See " + getLocationLineAndColumn(error.extra_) +
" for detail.\n";
1841 return formattedMessage;
1844 std::vector<CharReader::StructuredError>
1845 OurReader::getStructuredErrors()
const {
1846 std::vector<CharReader::StructuredError> allErrors;
1847 for (
const auto& error : errors_) {
1848 CharReader::StructuredError structured;
1849 structured.offset_start = error.token_.start_ - begin_;
1850 structured.offset_limit = error.token_.end_ - begin_;
1851 structured.message = error.message_;
1852 allErrors.push_back(structured);
1857 class OurCharReader :
public CharReader {
1860 OurCharReader(
bool collectComments, OurFeatures
const& features)
1862 std::unique_ptr<OurImpl>(new OurImpl(collectComments, features))) {}
1865 class OurImpl :
public Impl {
1867 OurImpl(
bool collectComments, OurFeatures
const& features)
1868 : 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();
1879 std::vector<CharReader::StructuredError>
1880 getStructuredErrors()
const override {
1881 return reader_.getStructuredErrors();
1885 bool const collectComments_;
1890 CharReaderBuilder::CharReaderBuilder() { setDefaults(&settings_); }
1891 CharReaderBuilder::~CharReaderBuilder() =
default;
1893 bool collectComments = settings_[
"collectComments"].asBool();
1894 OurFeatures features = OurFeatures::all();
1895 features.allowComments_ = settings_[
"allowComments"].asBool();
1896 features.allowTrailingCommas_ = settings_[
"allowTrailingCommas"].asBool();
1897 features.strictRoot_ = settings_[
"strictRoot"].asBool();
1898 features.allowDroppedNullPlaceholders_ =
1899 settings_[
"allowDroppedNullPlaceholders"].asBool();
1900 features.allowNumericKeys_ = settings_[
"allowNumericKeys"].asBool();
1901 features.allowSingleQuotes_ = settings_[
"allowSingleQuotes"].asBool();
1905 features.stackLimit_ =
static_cast<size_t>(settings_[
"stackLimit"].asUInt());
1906 features.failIfExtra_ = settings_[
"failIfExtra"].asBool();
1907 features.rejectDupKeys_ = settings_[
"rejectDupKeys"].asBool();
1908 features.allowSpecialFloats_ = settings_[
"allowSpecialFloats"].asBool();
1909 features.skipBom_ = settings_[
"skipBom"].asBool();
1910 return new OurCharReader(collectComments, features);
1914 static const auto& valid_keys = *
new std::set<String>{
1917 "allowTrailingCommas",
1919 "allowDroppedNullPlaceholders",
1921 "allowSingleQuotes",
1925 "allowSpecialFloats",
1928 for (
auto si = settings_.begin(); si != settings_.end(); ++si) {
1929 auto key = si.name();
1930 if (valid_keys.count(key))
1933 (*invalid)[key] = *si;
1937 return invalid ? invalid->
empty() :
true;
1941 return settings_[key];
1946 (*settings)[
"allowComments"] =
false;
1947 (*settings)[
"allowTrailingCommas"] =
false;
1948 (*settings)[
"strictRoot"] =
true;
1949 (*settings)[
"allowDroppedNullPlaceholders"] =
false;
1950 (*settings)[
"allowNumericKeys"] =
false;
1951 (*settings)[
"allowSingleQuotes"] =
false;
1952 (*settings)[
"stackLimit"] = 256;
1953 (*settings)[
"failIfExtra"] =
true;
1954 (*settings)[
"rejectDupKeys"] =
true;
1955 (*settings)[
"allowSpecialFloats"] =
false;
1956 (*settings)[
"skipBom"] =
true;
1962 (*settings)[
"collectComments"] =
true;
1963 (*settings)[
"allowComments"] =
true;
1964 (*settings)[
"allowTrailingCommas"] =
true;
1965 (*settings)[
"strictRoot"] =
false;
1966 (*settings)[
"allowDroppedNullPlaceholders"] =
false;
1967 (*settings)[
"allowNumericKeys"] =
false;
1968 (*settings)[
"allowSingleQuotes"] =
false;
1969 (*settings)[
"stackLimit"] = 256;
1970 (*settings)[
"failIfExtra"] =
false;
1971 (*settings)[
"rejectDupKeys"] =
false;
1972 (*settings)[
"allowSpecialFloats"] =
false;
1973 (*settings)[
"skipBom"] =
true;
1979 (*settings)[
"allowComments"] =
false;
1980 (*settings)[
"allowTrailingCommas"] =
false;
1981 (*settings)[
"strictRoot"] =
false;
1982 (*settings)[
"allowDroppedNullPlaceholders"] =
false;
1983 (*settings)[
"allowNumericKeys"] =
false;
1984 (*settings)[
"allowSingleQuotes"] =
false;
1985 (*settings)[
"stackLimit"] = 256;
1986 (*settings)[
"failIfExtra"] =
true;
1987 (*settings)[
"rejectDupKeys"] =
false;
1988 (*settings)[
"allowSpecialFloats"] =
false;
1989 (*settings)[
"skipBom"] =
false;
1993 std::vector<CharReader::StructuredError>
1994 CharReader::getStructuredErrors()
const {
1995 return _impl->getStructuredErrors();
1998 bool CharReader::parse(
char const* beginDoc,
char const* endDoc,
Value* root,
2000 return _impl->parse(beginDoc, endDoc, root, errs);
2009 ssin << sin.rdbuf();
2010 String doc = std::move(ssin).str();
2011 char const* begin = doc.data();
2012 char const* end = begin + doc.size();
2015 return reader->parse(begin, end, root, errs);
2023 throwRuntimeError(errs);
static String codePointToUTF8(unsigned int cp)
Converts a unicode code-point to UTF-8.
static constexpr LargestInt minLargestInt
Minimum signed integer value that can be stored in a Json::Value.
#define JSON_USE_EXCEPTION
#define JSON_API
If defined, indicates that the source file is amalgamated to prevent private header inclusion...
array value (ordered list)
std::basic_istringstream< String::value_type, String::traits_type, String::allocator_type > IStringStream
Json::LargestUInt LargestUInt
void setComment(const char *comment, size_t len, CommentPlacement placement)
Comments must be //... or /* ... */.
object value (collection of name/value pairs).
String getFormattedErrorMessages() const
Returns a user friendly string that list errors in the parsed document.
void swapPayload(Value &other)
Swap values but leave comments and source offsets in place.
static constexpr Int maxInt
Maximum signed int value that can be stored in a Json::Value.
bool empty() const
Return true if empty array, empty object, or null; otherwise, false.
std::basic_string< char, std::char_traits< char >, Allocator< char >> String
Features()
Initialize the configuration like JsonConfig::allFeatures;.
An error tagged with where in the JSON text it was encountered.
std::unique_ptr< CharReader > CharReaderPtr
std::vector< StructuredError > getStructuredErrors() const
Returns a vector of structured errors encountered while parsing.
static constexpr LargestUInt maxLargestUInt
Maximum unsigned integer value that can be stored in a Json::Value.
IStream & operator>>(IStream &, Value &)
Read from 'sin' into 'root'.
size_t & newlineScanByteCountForTesting()
bool allowComments_
true if comments are allowed. Default: true.
std::basic_ostringstream< String::value_type, String::traits_type, String::allocator_type > OStringStream
bool allowNumericKeys_
true if numeric object key are allowed. Default: false.
static size_t const stackLimit_g
bool good() const
Return whether there are any errors.
bool parse(const std::string &document, Value &root, bool collectComments=true)
Read a Value from a JSON document.
JSON (JavaScript Object Notation).
bool allowDroppedNullPlaceholders_
true if dropped null placeholders are allowed. Default: false.
bool pushError(const Value &value, const String &message)
Add a semantic error message.
static constexpr LargestInt maxLargestInt
Maximum signed integer value that can be stored in a Json::Value.
Interface for reading JSON from a char array.
Json::LargestInt LargestInt
void setOffsetStart(ptrdiff_t start)
ptrdiff_t getOffsetStart() const
static Features all()
A configuration that allows all features and assumes all strings are UTF-8.
a comment on the line after a value (only make sense for
ptrdiff_t getOffsetLimit() const
#define JSONCPP_DEPRECATED_STACK_LIMIT
bool parseFromStream(CharReader::Factory const &, IStream &, Value *root, String *errs)
Consume entire stream and use its begin/end.
void setOffsetLimit(ptrdiff_t limit)
static Features strictMode()
A configuration that is strictly compatible with the JSON specification.
bool strictRoot_
true if root must be either an array or an object value.
Build a CharReader implementation.
Configuration passed to reader and writer.
a comment placed on the line before a value
Reader()
Constructs a Reader allowing all features for parsing.
a comment just after a value on the same line
virtual CharReader * newCharReader() const =0
Allocate a CharReader via operator new().